gaoluyang
6 天以前 71a9eef518f2f2f1a1eb2fb90f2eb8ab7b155bc8
Merge remote-tracking branch 'origin/dev_天津军泰伟业' into dev_天津军泰伟业
已修改32个文件
27975 ■■■■ 文件已修改
src/components/QRCodeGenerator/index.vue 859 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/attendanceManagement/index.vue 2357 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/knowledgeBase/index.vue 1369 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/index.vue 2218 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue 769 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue 575 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/summary/index.vue 580 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/officeSupplies/index.vue 863 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/planTemplate/index.vue 1371 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/rpaManagement/index.vue 678 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/rulesRegulationsManagement/index.vue 1301 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/sealManagement/index.vue 1292 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/defectManagement/index.vue 332 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/components/formDia.vue 505 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/spareParts/index.vue 701 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/example/DynamicTableExample.vue 574 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fileManagement/bookshelf/index.vue 763 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockWarning/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/oaSystem/projectManagement/components/milestoneList.vue 466 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/oaSystem/projectManagement/components/taskTree.vue 1505 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/oaSystem/projectManagement/index.vue 792 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/oaSystem/projectManagement/projectDetail.vue 868 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementPlan/index.vue 1173 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManagement/productIdentifier/index.vue 1187 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productStructure/StructureEdit.vue 465 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrder/index.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/customerManagement/index.vue 684 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/orderManagement/index.vue 783 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/paymentShipping/index.vue 772 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesQuotation/index.vue 1349 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salespersonManagement/index.vue 595 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/tool/gen/importTable.vue 217 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/QRCodeGenerator/index.vue
@@ -1,70 +1,79 @@
<template>
  <div class="qr-code-generator">
    <!-- 二维码生成表单 -->
    <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="qr-form">
    <el-form :model="form"
             :rules="rules"
             ref="formRef"
             label-width="120px"
             class="qr-form">
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="标识类型" prop="type">
            <el-select v-model="form.type" placeholder="请选择标识类型" style="width: 100%">
              <el-option label="二维码" value="qrcode"></el-option>
              <el-option label="防伪码" value="security"></el-option>
          <el-form-item label="标识类型"
                        prop="type">
            <el-select v-model="form.type"
                       placeholder="请选择标识类型"
                       style="width: 100%">
              <el-option label="二维码"
                         value="qrcode"></el-option>
              <el-option label="防伪码"
                         value="security"></el-option>
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="内容" prop="content">
            <el-input
              v-model="form.content"
              placeholder="请输入要编码的内容"
              :type="form.type === 'security' ? 'textarea' : 'text'"
              :rows="form.type === 'security' ? 3 : 1"
            ></el-input>
          <el-form-item label="内容"
                        prop="content">
            <el-input v-model="form.content"
                      placeholder="请输入要编码的内容"
                      :type="form.type === 'security' ? 'textarea' : 'text'"
                      :rows="form.type === 'security' ? 3 : 1"></el-input>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="尺寸" prop="size">
            <el-input-number
              v-model="form.size"
              :min="100"
              :max="500"
              :step="50"
              style="width: 100%"
            ></el-input-number>
          <el-form-item label="尺寸"
                        prop="size">
            <el-input-number v-model="form.size"
                             :min="100"
                             :max="500"
                             :step="50"
                             style="width: 100%"></el-input-number>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="边距" prop="margin">
            <el-input-number
              v-model="form.margin"
              :min="0"
              :max="10"
              :step="1"
              style="width: 100%"
            ></el-input-number>
          <el-form-item label="边距"
                        prop="margin">
            <el-input-number v-model="form.margin"
                             :min="0"
                             :max="10"
                             :step="1"
                             style="width: 100%"></el-input-number>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="20">
        <el-col :span="12">
          <el-form-item label="前景色" prop="foregroundColor">
            <el-color-picker v-model="form.foregroundColor" style="width: 100%"></el-color-picker>
          <el-form-item label="前景色"
                        prop="foregroundColor">
            <el-color-picker v-model="form.foregroundColor"
                             style="width: 100%"></el-color-picker>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="背景色" prop="backgroundColor">
            <el-color-picker v-model="form.backgroundColor" style="width: 100%"></el-color-picker>
          <el-form-item label="背景色"
                        prop="backgroundColor">
            <el-color-picker v-model="form.backgroundColor"
                             style="width: 100%"></el-color-picker>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="20">
        <el-col :span="24">
          <el-form-item>
            <el-button type="primary" @click="generateCode" :loading="generating">
            <el-button type="primary"
                       @click="generateCode"
                       :loading="generating">
              生成{{ form.type === 'qrcode' ? '二维码' : '防伪码' }}
            </el-button>
            <el-button @click="resetForm">重置</el-button>
@@ -72,18 +81,17 @@
        </el-col>
      </el-row>
    </el-form>
    <!-- 生成的码显示区域 -->
    <div v-if="generatedCodeUrl" class="code-display">
    <div v-if="generatedCodeUrl"
         class="code-display">
      <el-divider content-position="center">
        {{ form.type === 'qrcode' ? '生成的二维码' : '生成的防伪码' }}
      </el-divider>
      <div class="code-container">
        <div class="code-image">
          <img :src="generatedCodeUrl" :alt="form.type === 'qrcode' ? '二维码' : '防伪码'" />
          <img :src="generatedCodeUrl"
               :alt="form.type === 'qrcode' ? '二维码' : '防伪码'" />
        </div>
        <div class="code-info">
          <p><strong>内容:</strong>{{ form.content }}</p>
          <p><strong>类型:</strong>{{ form.type === 'qrcode' ? '二维码' : '防伪码' }}</p>
@@ -91,60 +99,71 @@
          <p><strong>生成时间:</strong>{{ generateTime }}</p>
        </div>
      </div>
      <div class="code-actions">
        <el-button type="success" @click="downloadCode" icon="Download">
        <el-button type="success"
                   @click="downloadCode"
                   icon="Download">
          下载图片
        </el-button>
        <el-button type="primary" @click="copyToClipboard" icon="CopyDocument">
        <el-button type="primary"
                   @click="copyToClipboard"
                   icon="CopyDocument">
          复制内容
        </el-button>
        <el-button @click="printCode" icon="Printer">
        <el-button @click="printCode"
                   icon="Printer">
          打印
        </el-button>
      </div>
    </div>
    <!-- 批量生成对话框 -->
    <el-dialog v-model="batchDialogVisible" title="批量生成" width="600px">
      <el-form :model="batchForm" label-width="120px">
    <el-dialog v-model="batchDialogVisible"
               title="批量生成"
               width="600px">
      <el-form :model="batchForm"
               label-width="120px">
        <el-form-item label="生成数量">
          <el-input-number v-model="batchForm.quantity" :min="1" :max="100" style="width: 100%"></el-input-number>
          <el-input-number v-model="batchForm.quantity"
                           :min="1"
                           :max="100"
                           style="width: 100%"></el-input-number>
        </el-form-item>
        <el-form-item label="前缀">
          <el-input v-model="batchForm.prefix" placeholder="请输入前缀,如:PROD_"></el-input>
          <el-input v-model="batchForm.prefix"
                    placeholder="请输入前缀,如:PROD_"></el-input>
        </el-form-item>
        <el-form-item label="起始编号">
          <el-input-number v-model="batchForm.startNumber" :min="1" style="width: 100%"></el-input-number>
          <el-input-number v-model="batchForm.startNumber"
                           :min="1"
                           style="width: 100%"></el-input-number>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="generateBatchCodes">开始生成</el-button>
          <el-button @click="batchDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="generateBatchCodes">开始生成</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 批量生成结果 -->
    <div v-if="batchCodes.length > 0" class="batch-results">
    <div v-if="batchCodes.length > 0"
         class="batch-results">
      <el-divider content-position="center">批量生成结果</el-divider>
      <div class="batch-grid">
        <div
          v-for="(code, index) in batchCodes"
          :key="index"
          class="batch-item"
        >
          <img :src="code.url" :alt="code.content" />
        <div v-for="(code, index) in batchCodes"
             :key="index"
             class="batch-item">
          <img :src="code.url"
               :alt="code.content" />
          <p class="batch-content">{{ code.content }}</p>
          <el-button size="small" @click="downloadSingleCode(code)">下载</el-button>
          <el-button size="small"
                     @click="downloadSingleCode(code)">下载</el-button>
        </div>
      </div>
      <div class="batch-actions">
        <el-button type="success" @click="downloadAllCodes">下载全部</el-button>
        <el-button type="success"
                   @click="downloadAllCodes">下载全部</el-button>
        <el-button @click="clearBatchCodes">清空结果</el-button>
      </div>
    </div>
@@ -152,390 +171,396 @@
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import QRCode from 'qrcode'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Download, CopyDocument, Printer } from '@element-plus/icons-vue'
  import { ref, reactive, computed, onMounted } from "vue";
  import QRCode from "qrcode";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { Download, CopyDocument, Printer } from "@element-plus/icons-vue";
// 定义组件名称
defineOptions({
  name: 'QRCodeGenerator'
})
  // 定义组件名称
  defineOptions({
    name: "QRCodeGenerator",
  });
// 表单数据
const form = reactive({
  type: 'qrcode',
  content: '',
  size: 200,
  margin: 2,
  foregroundColor: '#000000',
  backgroundColor: '#FFFFFF'
})
  // 表单数据
  const form = reactive({
    type: "qrcode",
    content: "",
    size: 200,
    margin: 2,
    foregroundColor: "#000000",
    backgroundColor: "#FFFFFF",
  });
// 表单验证规则
const rules = {
  type: [{ required: true, message: '请选择标识类型', trigger: 'change' }],
  content: [{ required: true, message: '请输入内容', trigger: 'blur' }]
}
  // 表单验证规则
  const rules = {
    type: [{ required: true, message: "请选择标识类型", trigger: "change" }],
    content: [{ required: true, message: "请输入内容", trigger: "blur" }],
  };
// 响应式数据
const formRef = ref()
const generating = ref(false)
const generatedCodeUrl = ref('')
const generateTime = ref('')
const batchDialogVisible = ref(false)
const batchForm = reactive({
  quantity: 10,
  prefix: '',
  startNumber: 1
})
const batchCodes = ref([])
  // 响应式数据
  const formRef = ref();
  const generating = ref(false);
  const generatedCodeUrl = ref("");
  const generateTime = ref("");
  const batchDialogVisible = ref(false);
  const batchForm = reactive({
    quantity: 10,
    prefix: "",
    startNumber: 1,
  });
  const batchCodes = ref([]);
// 生成二维码或防伪码
const generateCode = async () => {
  try {
    await formRef.value.validate()
    if (!form.content.trim()) {
      ElMessage.warning('请输入要编码的内容')
      return
    }
    generating.value = true
    if (form.type === 'qrcode') {
      // 生成二维码
      generatedCodeUrl.value = await QRCode.toDataURL(form.content, {
        width: form.size,
        margin: form.margin,
        color: {
          dark: form.foregroundColor,
          light: form.backgroundColor
        },
        errorCorrectionLevel: 'M'
      })
    } else {
      // 生成防伪码(使用二维码技术,但内容格式不同)
      const securityContent = generateSecurityCode(form.content)
      generatedCodeUrl.value = await QRCode.toDataURL(securityContent, {
        width: form.size,
        margin: form.margin,
        color: {
          dark: form.foregroundColor,
          light: form.backgroundColor
        },
        errorCorrectionLevel: 'H' // 防伪码使用最高纠错级别
      })
    }
    generateTime.value = new Date().toLocaleString()
    ElMessage.success('生成成功!')
  } catch (error) {
    console.error('生成失败:', error)
    ElMessage.error('生成失败:' + error.message)
  } finally {
    generating.value = false
  }
}
  // 生成二维码或防伪码
  const generateCode = async () => {
    try {
      await formRef.value.validate();
// 生成防伪码内容
const generateSecurityCode = (content) => {
  const timestamp = Date.now()
  const random = Math.random().toString(36).substr(2, 8)
  return `SEC_${content}_${timestamp}_${random}`
}
      if (!form.content.trim()) {
        ElMessage.warning("请输入要编码的内容");
        return;
      }
// 下载生成的码
const downloadCode = () => {
  if (!generatedCodeUrl.value) {
    ElMessage.warning('请先生成码')
    return
  }
  const a = document.createElement('a')
  a.href = generatedCodeUrl.value
  a.download = `${form.type === 'qrcode' ? '二维码' : '防伪码'}_${new Date().getTime()}.png`
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
  ElMessage.success('下载成功!')
}
      generating.value = true;
// 复制内容到剪贴板
const copyToClipboard = async () => {
  try {
    await navigator.clipboard.writeText(form.content)
    ElMessage.success('内容已复制到剪贴板')
  } catch (error) {
    // 降级方案
    const textArea = document.createElement('textarea')
    textArea.value = form.content
    document.body.appendChild(textArea)
    textArea.select()
    document.execCommand('copy')
    document.body.removeChild(textArea)
    ElMessage.success('内容已复制到剪贴板')
  }
}
// 打印码
const printCode = () => {
  if (!generatedCodeUrl.value) {
    ElMessage.warning('请先生成码')
    return
  }
  const printWindow = window.open('', '_blank')
  printWindow.document.write(`
    <html>
      <head>
        <title>打印${form.type === 'qrcode' ? '二维码' : '防伪码'}</title>
        <style>
          body { text-align: center; padding: 20px; }
          img { max-width: 100%; height: auto; }
          .info { margin: 20px 0; }
        </style>
      </head>
      <body>
        <h2>${form.type === 'qrcode' ? '二维码' : '防伪码'}</h2>
        <img src="${generatedCodeUrl.value}" alt="${form.type === 'qrcode' ? '二维码' : '防伪码'}" />
        <div class="info">
          <p><strong>内容:</strong>${form.content}</p>
          <p><strong>生成时间:</strong>${generateTime.value}</p>
        </div>
      </body>
    </html>
  `)
  printWindow.document.close()
  printWindow.print()
}
// 重置表单
const resetForm = () => {
  formRef.value.resetFields()
  generatedCodeUrl.value = ''
  generateTime.value = ''
  batchCodes.value = []
}
// 批量生成
const generateBatchCodes = async () => {
  if (!batchForm.prefix.trim()) {
    ElMessage.warning('请输入前缀')
    return
  }
  batchCodes.value = []
  generating.value = true
  try {
    for (let i = 0; i < batchForm.quantity; i++) {
      const number = batchForm.startNumber + i
      const content = `${batchForm.prefix}${number.toString().padStart(6, '0')}`
      let codeUrl
      if (form.type === 'qrcode') {
        codeUrl = await QRCode.toDataURL(content, {
      if (form.type === "qrcode") {
        // 生成二维码
        generatedCodeUrl.value = await QRCode.toDataURL(form.content, {
          width: form.size,
          margin: form.margin,
          color: {
            dark: form.foregroundColor,
            light: form.backgroundColor
          }
        })
            light: form.backgroundColor,
          },
          errorCorrectionLevel: "M",
        });
      } else {
        const securityContent = generateSecurityCode(content)
        codeUrl = await QRCode.toDataURL(securityContent, {
        // 生成防伪码(使用二维码技术,但内容格式不同)
        const securityContent = generateSecurityCode(form.content);
        generatedCodeUrl.value = await QRCode.toDataURL(securityContent, {
          width: form.size,
          margin: form.margin,
          color: {
            dark: form.foregroundColor,
            light: form.backgroundColor
          }
        })
            light: form.backgroundColor,
          },
          errorCorrectionLevel: "H", // 防伪码使用最高纠错级别
        });
      }
      batchCodes.value.push({
        content,
        url: codeUrl
      })
      generateTime.value = new Date().toLocaleString();
      ElMessage.success("生成成功!");
    } catch (error) {
      console.error("生成失败:", error);
      ElMessage.error("生成失败:" + error.message);
    } finally {
      generating.value = false;
    }
    ElMessage.success(`批量生成完成,共生成 ${batchForm.quantity} 个码`)
    batchDialogVisible.value = false
  } catch (error) {
    console.error('批量生成失败:', error)
    ElMessage.error('批量生成失败:' + error.message)
  } finally {
    generating.value = false
  }
}
  };
// 下载单个批量生成的码
const downloadSingleCode = (code) => {
  const a = document.createElement('a')
  a.href = code.url
  a.download = `${code.content}.png`
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
}
  // 生成防伪码内容
  const generateSecurityCode = content => {
    const timestamp = Date.now();
    const random = Math.random().toString(36).substr(2, 8);
    return `SEC_${content}_${timestamp}_${random}`;
  };
// 下载所有批量生成的码
const downloadAllCodes = async () => {
  if (batchCodes.value.length === 0) {
    ElMessage.warning('没有可下载的码')
    return
  }
  try {
    // 使用JSZip打包下载
    const JSZip = await import('jszip')
    const zip = new JSZip.default()
    batchCodes.value.forEach((code, index) => {
      // 将base64转换为blob
      const base64Data = code.url.split(',')[1]
      const byteCharacters = atob(base64Data)
      const byteNumbers = new Array(byteCharacters.length)
      for (let i = 0; i < byteCharacters.length; i++) {
        byteNumbers[i] = byteCharacters.charCodeAt(i)
  // 下载生成的码
  const downloadCode = () => {
    if (!generatedCodeUrl.value) {
      ElMessage.warning("请先生成码");
      return;
    }
    const a = document.createElement("a");
    a.href = generatedCodeUrl.value;
    a.download = `${
      form.type === "qrcode" ? "二维码" : "防伪码"
    }_${new Date().getTime()}.png`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    ElMessage.success("下载成功!");
  };
  // 复制内容到剪贴板
  const copyToClipboard = async () => {
    try {
      await navigator.clipboard.writeText(form.content);
      ElMessage.success("内容已复制到剪贴板");
    } catch (error) {
      // 降级方案
      const textArea = document.createElement("textarea");
      textArea.value = form.content;
      document.body.appendChild(textArea);
      textArea.select();
      document.execCommand("copy");
      document.body.removeChild(textArea);
      ElMessage.success("内容已复制到剪贴板");
    }
  };
  // 打印码
  const printCode = () => {
    if (!generatedCodeUrl.value) {
      ElMessage.warning("请先生成码");
      return;
    }
    const printWindow = window.open("", "_blank");
    printWindow.document.write(`
      <html>
        <head>
          <title>打印${form.type === "qrcode" ? "二维码" : "防伪码"}</title>
          <style>
            body { text-align: center; padding: 20px; }
            img { max-width: 100%; height: auto; }
            .info { margin: 20px 0; }
          </style>
        </head>
        <body>
          <h2>${form.type === "qrcode" ? "二维码" : "防伪码"}</h2>
          <img src="${generatedCodeUrl.value}" alt="${
      form.type === "qrcode" ? "二维码" : "防伪码"
    }" />
          <div class="info">
            <p><strong>内容:</strong>${form.content}</p>
            <p><strong>生成时间:</strong>${generateTime.value}</p>
          </div>
        </body>
      </html>
    `);
    printWindow.document.close();
    printWindow.print();
  };
  // 重置表单
  const resetForm = () => {
    formRef.value.resetFields();
    generatedCodeUrl.value = "";
    generateTime.value = "";
    batchCodes.value = [];
  };
  // 批量生成
  const generateBatchCodes = async () => {
    if (!batchForm.prefix.trim()) {
      ElMessage.warning("请输入前缀");
      return;
    }
    batchCodes.value = [];
    generating.value = true;
    try {
      for (let i = 0; i < batchForm.quantity; i++) {
        const number = batchForm.startNumber + i;
        const content = `${batchForm.prefix}${number
          .toString()
          .padStart(6, "0")}`;
        let codeUrl;
        if (form.type === "qrcode") {
          codeUrl = await QRCode.toDataURL(content, {
            width: form.size,
            margin: form.margin,
            color: {
              dark: form.foregroundColor,
              light: form.backgroundColor,
            },
          });
        } else {
          const securityContent = generateSecurityCode(content);
          codeUrl = await QRCode.toDataURL(securityContent, {
            width: form.size,
            margin: form.margin,
            color: {
              dark: form.foregroundColor,
              light: form.backgroundColor,
            },
          });
        }
        batchCodes.value.push({
          content,
          url: codeUrl,
        });
      }
      const byteArray = new Uint8Array(byteNumbers)
      zip.file(`${code.content}.png`, byteArray)
    })
    const content = await zip.generateAsync({ type: 'blob' })
    const a = document.createElement('a')
    a.href = URL.createObjectURL(content)
    a.download = `批量${form.type === 'qrcode' ? '二维码' : '防伪码'}_${new Date().getTime()}.zip`
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(a.href)
    ElMessage.success('批量下载完成!')
  } catch (error) {
    console.error('批量下载失败:', error)
    ElMessage.error('批量下载失败,请逐个下载')
  }
}
// 清空批量生成结果
const clearBatchCodes = () => {
  batchCodes.value = []
}
      ElMessage.success(`批量生成完成,共生成 ${batchForm.quantity} 个码`);
      batchDialogVisible.value = false;
    } catch (error) {
      console.error("批量生成失败:", error);
      ElMessage.error("批量生成失败:" + error.message);
    } finally {
      generating.value = false;
    }
  };
// 暴露方法给父组件
defineExpose({
  generateCode,
  downloadCode,
  resetForm,
  form
})
  // 下载单个批量生成的码
  const downloadSingleCode = code => {
    const a = document.createElement("a");
    a.href = code.url;
    a.download = `${code.content}.png`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };
  // 下载所有批量生成的码
  const downloadAllCodes = async () => {
    if (batchCodes.value.length === 0) {
      ElMessage.warning("没有可下载的码");
      return;
    }
    try {
      // 使用JSZip打包下载
      const JSZip = await import("jszip");
      const zip = new JSZip.default();
      batchCodes.value.forEach((code, index) => {
        // 将base64转换为blob
        const base64Data = code.url.split(",")[1];
        const byteCharacters = atob(base64Data);
        const byteNumbers = new Array(byteCharacters.length);
        for (let i = 0; i < byteCharacters.length; i++) {
          byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        zip.file(`${code.content}.png`, byteArray);
      });
      const content = await zip.generateAsync({ type: "blob" });
      const a = document.createElement("a");
      a.href = URL.createObjectURL(content);
      a.download = `批量${
        form.type === "qrcode" ? "二维码" : "防伪码"
      }_${new Date().getTime()}.zip`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(a.href);
      ElMessage.success("批量下载完成!");
    } catch (error) {
      console.error("批量下载失败:", error);
      ElMessage.error("批量下载失败,请逐个下载");
    }
  };
  // 清空批量生成结果
  const clearBatchCodes = () => {
    batchCodes.value = [];
  };
  // 暴露方法给父组件
  defineExpose({
    generateCode,
    downloadCode,
    resetForm,
    form,
  });
</script>
<style scoped>
.qr-code-generator {
  padding: 20px;
}
  .qr-code-generator {
    padding: 20px;
  }
.qr-form {
  background: #f8f9fa;
  padding: 20px;
  border-radius: 8px;
  margin-bottom: 20px;
}
  .qr-form {
    background: #f8f9fa;
    padding: 20px;
    border-radius: 8px;
    margin-bottom: 20px;
  }
.code-display {
  margin-top: 30px;
}
  .code-display {
    margin-top: 30px;
  }
.code-container {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 40px;
  margin: 20px 0;
}
.code-image img {
  border: 2px solid #e0e0e0;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.code-info {
  text-align: left;
  min-width: 200px;
}
.code-info p {
  margin: 8px 0;
  color: #666;
}
.code-actions {
  text-align: center;
  margin: 20px 0;
}
.code-actions .el-button {
  margin: 0 10px;
}
.batch-results {
  margin-top: 30px;
}
.batch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 20px;
  margin: 20px 0;
}
.batch-item {
  text-align: center;
  padding: 15px;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  background: #fff;
}
.batch-item img {
  width: 100px;
  height: 100px;
  margin-bottom: 10px;
}
.batch-content {
  font-size: 12px;
  color: #666;
  margin: 10px 0;
  word-break: break-all;
}
.batch-actions {
  text-align: center;
  margin: 20px 0;
}
.batch-actions .el-button {
  margin: 0 10px;
}
@media (max-width: 768px) {
  .code-container {
    flex-direction: column;
    align-items: center;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    gap: 40px;
    margin: 20px 0;
  }
  .code-image img {
    border: 2px solid #e0e0e0;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  }
  .code-info {
    text-align: left;
    min-width: 200px;
  }
  .code-info p {
    margin: 8px 0;
    color: #666;
  }
  .code-actions {
    text-align: center;
    margin: 20px 0;
  }
  .code-actions .el-button {
    margin: 0 10px;
  }
  .batch-results {
    margin-top: 30px;
  }
  .batch-grid {
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 20px;
    margin: 20px 0;
  }
}
  .batch-item {
    text-align: center;
    padding: 15px;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    background: #fff;
  }
  .batch-item img {
    width: 100px;
    height: 100px;
    margin-bottom: 10px;
  }
  .batch-content {
    font-size: 12px;
    color: #666;
    margin: 10px 0;
    word-break: break-all;
  }
  .batch-actions {
    text-align: center;
    margin: 20px 0;
  }
  .batch-actions .el-button {
    margin: 0 10px;
  }
  @media (max-width: 768px) {
    .code-container {
      flex-direction: column;
      align-items: center;
    }
    .batch-grid {
      grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    }
  }
</style>
src/views/collaborativeApproval/attendanceManagement/index.vue
@@ -1,310 +1,419 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" type="border-card">
    <el-tabs v-model="activeTab"
             type="border-card">
      <!-- 假期设置 -->
      <el-tab-pane label="假期设置" name="holiday">
      <el-tab-pane label="假期设置"
                   name="holiday">
        <div class="tab-content">
          <el-button type="primary" @click="openDialog('holiday', 'add')">新增假期</el-button>
          <el-table :data="holidayData" border style="width: 100%; margin-top: 20px;">
            <el-table-column prop="name" label="假期名称" />
            <el-table-column prop="type" label="假期类型">
          <el-button type="primary"
                     @click="openDialog('holiday', 'add')">新增假期</el-button>
          <el-table :data="holidayData"
                    border
                    style="width: 100%; margin-top: 20px;">
            <el-table-column prop="name"
                             label="假期名称" />
            <el-table-column prop="type"
                             label="假期类型">
              <template #default="scope">
                <el-tag :type="getTagType(scope.row.type)">{{ getTypeLabel(scope.row.type) }}</el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="startDate" label="开始日期"  />
            <el-table-column prop="endDate" label="结束日期"  />
            <el-table-column prop="days" label="天数"  align="center" />
            <el-table-column prop="status" label="状态" >
            <el-table-column prop="startDate"
                             label="开始日期" />
            <el-table-column prop="endDate"
                             label="结束日期" />
            <el-table-column prop="days"
                             label="天数"
                             align="center" />
            <el-table-column prop="status"
                             label="状态">
              <template #default="scope">
                <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
                  {{ scope.row.status === 'active' ? '启用' : '停用' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="操作" fixed="right">
            <el-table-column label="操作"
                             fixed="right">
              <template #default="scope">
                <el-button type="primary" size="small" @click="openDialog('holiday', 'edit', scope.row)">编辑</el-button>
                <el-button type="danger" size="small" @click="deleteItem('holiday', scope.row)">删除</el-button>
                <el-button type="primary"
                           size="small"
                           @click="openDialog('holiday', 'edit', scope.row)">编辑</el-button>
                <el-button type="danger"
                           size="small"
                           @click="deleteItem('holiday', scope.row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </el-tab-pane>
      <!-- 年假设置 -->
      <el-tab-pane label="年假设置" name="annual">
      <el-tab-pane label="年假设置"
                   name="annual">
        <div class="tab-content">
          <el-button type="primary" @click="openDialog('annual', 'add')">新增年假规则</el-button>
          <el-table :data="annualData" border style="width: 100%; margin-top: 20px;">
            <el-table-column prop="employeeType" label="员工类型">
          <el-button type="primary"
                     @click="openDialog('annual', 'add')">新增年假规则</el-button>
          <el-table :data="annualData"
                    border
                    style="width: 100%; margin-top: 20px;">
            <el-table-column prop="employeeType"
                             label="员工类型">
              <template #default="scope">
                <el-tag :type="getTagType(scope.row.employeeType)">{{ getTypeLabel(scope.row.employeeType) }}</el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="workYears" label="工作年限" />
            <el-table-column prop="annualDays" label="年假天数" align="center" />
            <el-table-column prop="maxCarryOver" label="最大结转天数" align="center" />
            <el-table-column prop="status" label="状态">
            <el-table-column prop="workYears"
                             label="工作年限" />
            <el-table-column prop="annualDays"
                             label="年假天数"
                             align="center" />
            <el-table-column prop="maxCarryOver"
                             label="最大结转天数"
                             align="center" />
            <el-table-column prop="status"
                             label="状态">
              <template #default="scope">
                <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
                  {{ scope.row.status === 'active' ? '启用' : '停用' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="操作" fixed="right">
            <el-table-column label="操作"
                             fixed="right">
              <template #default="scope">
                <el-button type="primary" size="small" @click="openDialog('annual', 'edit', scope.row)">编辑</el-button>
                <el-button type="danger" size="small" @click="deleteItem('annual', scope.row)">删除</el-button>
                <el-button type="primary"
                           size="small"
                           @click="openDialog('annual', 'edit', scope.row)">编辑</el-button>
                <el-button type="danger"
                           size="small"
                           @click="deleteItem('annual', scope.row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </el-tab-pane>
      <!-- 加班设置 -->
      <el-tab-pane label="加班设置" name="overtime">
      <el-tab-pane label="加班设置"
                   name="overtime">
        <div class="tab-content">
          <el-button type="primary" @click="openDialog('overtime', 'add')">新增加班规则</el-button>
          <el-table :data="overtimeData" border style="width: 100%; margin-top: 20px;">
            <el-table-column prop="name" label="规则名称" />
            <el-table-column prop="type" label="加班类型" >
          <el-button type="primary"
                     @click="openDialog('overtime', 'add')">新增加班规则</el-button>
          <el-table :data="overtimeData"
                    border
                    style="width: 100%; margin-top: 20px;">
            <el-table-column prop="name"
                             label="规则名称" />
            <el-table-column prop="type"
                             label="加班类型">
              <template #default="scope">
                <el-tag :type="getTagType(scope.row.type)">{{ getTypeLabel(scope.row.type) }}</el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="startTime" label="开始时间"  />
            <el-table-column prop="endTime" label="结束时间"  />
            <el-table-column prop="rate" label="倍率" align="center" />
            <el-table-column prop="status" label="状态" >
            <el-table-column prop="startTime"
                             label="开始时间" />
            <el-table-column prop="endTime"
                             label="结束时间" />
            <el-table-column prop="rate"
                             label="倍率"
                             align="center" />
            <el-table-column prop="status"
                             label="状态">
              <template #default="scope">
                <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
                  {{ scope.row.status === 'active' ? '启用' : '停用' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="操作" fixed="right">
            <el-table-column label="操作"
                             fixed="right">
              <template #default="scope">
                <el-button type="primary" size="small" @click="openDialog('overtime', 'edit', scope.row)">编辑</el-button>
                <el-button type="danger" size="small" @click="deleteItem('overtime', scope.row)">删除</el-button>
                <el-button type="primary"
                           size="small"
                           @click="openDialog('overtime', 'edit', scope.row)">编辑</el-button>
                <el-button type="danger"
                           size="small"
                           @click="deleteItem('overtime', scope.row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </el-tab-pane>
      <!-- 上班时间设置 -->
      <el-tab-pane label="上班时间设置" name="worktime">
      <el-tab-pane label="上班时间设置"
                   name="worktime">
        <div class="tab-content">
          <el-button type="primary" @click="openDialog('worktime', 'add')">新增时间段</el-button>
          <el-table :data="worktimeData" border style="width: 100%; margin-top: 20px;">
            <el-table-column prop="name" label="时间段名称"  />
            <el-table-column prop="startTime" label="上班时间"/>
            <el-table-column prop="endTime" label="下班时间" />
            <el-table-column prop="flexibleStart" label="弹性上班">
          <el-button type="primary"
                     @click="openDialog('worktime', 'add')">新增时间段</el-button>
          <el-table :data="worktimeData"
                    border
                    style="width: 100%; margin-top: 20px;">
            <el-table-column prop="name"
                             label="时间段名称" />
            <el-table-column prop="startTime"
                             label="上班时间" />
            <el-table-column prop="endTime"
                             label="下班时间" />
            <el-table-column prop="flexibleStart"
                             label="弹性上班">
              <template #default="scope">
                <el-tag :type="scope.row.flexibleStart === 'true' ? 'success' : 'info'">
                  {{ scope.row.flexibleStart === 'true' ? '是' : '否' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="flexibleMinutes" label="弹性时间(分钟)" width="120" align="center" />
            <el-table-column prop="status" label="状态" >
            <el-table-column prop="flexibleMinutes"
                             label="弹性时间(分钟)"
                             width="120"
                             align="center" />
            <el-table-column prop="status"
                             label="状态">
              <template #default="scope">
                <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
                  {{ scope.row.status === 'active' ? '启用' : '停用' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="操作" fixed="right">
            <el-table-column label="操作"
                             fixed="right">
              <template #default="scope">
                <el-button type="primary" size="small" @click="openDialog('worktime', 'edit', scope.row)">编辑</el-button>
                <el-button type="danger" size="small" @click="deleteItem('worktime', scope.row)">删除</el-button>
                <el-button type="primary"
                           size="small"
                           @click="openDialog('worktime', 'edit', scope.row)">编辑</el-button>
                <el-button type="danger"
                           size="small"
                           @click="deleteItem('worktime', scope.row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </el-tab-pane>
      <!-- 打卡记录 -->
      <el-tab-pane label="打卡记录" name="attendance">
      <el-tab-pane label="打卡记录"
                   name="attendance">
        <div class="tab-content">
          <div style="margin-bottom: 20px;">
            <el-date-picker
              v-model="attendanceDate"
              type="date"
              placeholder="选择日期"
              format="YYYY-MM-DD"
              value-format="YYYY-MM-DD"
              style="margin-right: 10px;"
              @change="filterAttendanceData"
            />
            <el-select
              v-model="attendanceStatus"
              placeholder="选择状态"
              style="width: 120px; margin-right: 10px;"
              @change="filterAttendanceData"
            >
              <el-option label="全部" value="" />
              <el-option label="正常" value="normal" />
              <el-option label="迟到" value="late" />
              <el-option label="早退" value="early" />
              <el-option label="缺勤" value="absent" />
            <el-date-picker v-model="attendanceDate"
                            type="date"
                            placeholder="选择日期"
                            format="YYYY-MM-DD"
                            value-format="YYYY-MM-DD"
                            style="margin-right: 10px;"
                            @change="filterAttendanceData" />
            <el-select v-model="attendanceStatus"
                       placeholder="选择状态"
                       style="width: 120px; margin-right: 10px;"
                       @change="filterAttendanceData">
              <el-option label="全部"
                         value="" />
              <el-option label="正常"
                         value="normal" />
              <el-option label="迟到"
                         value="late" />
              <el-option label="早退"
                         value="early" />
              <el-option label="缺勤"
                         value="absent" />
            </el-select>
            <el-button type="primary" @click="exportAttendance">导出记录</el-button>
            <el-button type="primary"
                       @click="exportAttendance">导出记录</el-button>
          </div>
          <el-table :data="filteredAttendanceData" border style="width: 100%;">
            <el-table-column prop="employeeName" label="员工姓名" width="120" />
            <el-table-column prop="department" label="部门" width="120" />
            <el-table-column prop="date" label="日期" width="120" />
            <el-table-column prop="clockInTime" label="上班打卡" width="120" />
            <el-table-column prop="clockOutTime" label="下班打卡" width="120" />
            <el-table-column prop="workHours" label="工作时长" width="100" align="center" />
            <el-table-column prop="status" label="状态" width="100" align="center">
          <el-table :data="filteredAttendanceData"
                    border
                    style="width: 100%;">
            <el-table-column prop="employeeName"
                             label="员工姓名"
                             width="120" />
            <el-table-column prop="department"
                             label="部门"
                             width="120" />
            <el-table-column prop="date"
                             label="日期"
                             width="120" />
            <el-table-column prop="clockInTime"
                             label="上班打卡"
                             width="120" />
            <el-table-column prop="clockOutTime"
                             label="下班打卡"
                             width="120" />
            <el-table-column prop="workHours"
                             label="工作时长"
                             width="100"
                             align="center" />
            <el-table-column prop="status"
                             label="状态"
                             width="100"
                             align="center">
              <template #default="scope">
                <el-tag :type="getAttendanceTagType(scope.row.status)">{{ getAttendanceStatusLabel(scope.row.status) }}</el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="location" label="打卡地点" width="150" />
            <el-table-column prop="remark" label="备注" min-width="150" />
            <el-table-column prop="location"
                             label="打卡地点"
                             width="150" />
            <el-table-column prop="remark"
                             label="备注"
                             min-width="150" />
          </el-table>
        </div>
      </el-tab-pane>
    </el-tabs>
    <!-- 通用弹窗 -->
    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
        <el-form-item label="名称" prop="name" v-if="currentType !== 'annual'">
          <el-input v-model="form.name" placeholder="请输入名称" />
    <el-dialog v-model="dialogVisible"
               :title="dialogTitle"
               width="600px">
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="100px">
        <el-form-item label="名称"
                      prop="name"
                      v-if="currentType !== 'annual'">
          <el-input v-model="form.name"
                    placeholder="请输入名称" />
        </el-form-item>
        <el-form-item label="类型" prop="type" v-if="currentType === 'holiday' || currentType === 'overtime'">
          <el-select v-model="form.type" placeholder="请选择类型" style="width: 100%">
            <el-option
              v-for="option in getTypeOptions()"
              :key="option.value"
              :label="option.label"
              :value="option.value"
            />
        <el-form-item label="类型"
                      prop="type"
                      v-if="currentType === 'holiday' || currentType === 'overtime'">
          <el-select v-model="form.type"
                     placeholder="请选择类型"
                     style="width: 100%">
            <el-option v-for="option in getTypeOptions()"
                       :key="option.value"
                       :label="option.label"
                       :value="option.value" />
          </el-select>
        </el-form-item>
        <el-form-item label="员工类型" prop="employeeType" v-if="currentType === 'annual'">
          <el-select v-model="form.employeeType" placeholder="请选择员工类型" style="width: 100%">
        <el-form-item label="员工类型"
                      prop="employeeType"
                      v-if="currentType === 'annual'">
          <el-select v-model="form.employeeType"
                     placeholder="请选择员工类型"
                     style="width: 100%">
            <!-- <el-option label="正式员工" value="regular" />
            <el-option label="试用期员工" value="probation" />
            <el-option label="实习生" value="intern" /> -->
            <el-option
              v-for="option in getTypeOptions()"
              :key="option.value"
              :label="option.label"
              :value="option.value"
            />
            <el-option v-for="option in getTypeOptions()"
                       :key="option.value"
                       :label="option.label"
                       :value="option.value" />
          </el-select>
        </el-form-item>
        <el-form-item label="工作年限" prop="workYears" v-if="currentType === 'annual'">
          <el-input v-model="form.workYears" placeholder="如:1-3年、3-5年等" />
        <el-form-item label="工作年限"
                      prop="workYears"
                      v-if="currentType === 'annual'">
          <el-input v-model="form.workYears"
                    placeholder="如:1-3年、3-5年等" />
        </el-form-item>
        <el-form-item label="年假天数" prop="annualDays" v-if="currentType === 'annual'">
          <el-input-number v-model="form.annualDays" :min="0" :max="365" style="width: 100%" />
        <el-form-item label="年假天数"
                      prop="annualDays"
                      v-if="currentType === 'annual'">
          <el-input-number v-model="form.annualDays"
                           :min="0"
                           :max="365"
                           style="width: 100%" />
        </el-form-item>
        <el-form-item label="最大结转天数" prop="maxCarryOver" v-if="currentType === 'annual'">
          <el-input-number v-model="form.maxCarryOver" :min="0" :max="30" style="width: 100%" />
        <el-form-item label="最大结转天数"
                      prop="maxCarryOver"
                      v-if="currentType === 'annual'">
          <el-input-number v-model="form.maxCarryOver"
                           :min="0"
                           :max="30"
                           style="width: 100%" />
        </el-form-item>
                          <el-form-item label="日期范围" prop="dateRange" v-if="currentType === 'holiday'">
           <el-date-picker
             v-model="form.dateRange"
             type="daterange"
             range-separator="至"
             start-placeholder="开始日期"
             end-placeholder="结束日期"
             style="width: 100%"
             @change="calculateDays"
           />
         </el-form-item>
        <el-form-item label="天数" prop="days" v-if="currentType === 'holiday'">
          <el-input-number v-model="form.days" :min="0" style="width: 100%" />
        <el-form-item label="日期范围"
                      prop="dateRange"
                      v-if="currentType === 'holiday'">
          <el-date-picker v-model="form.dateRange"
                          type="daterange"
                          range-separator="至"
                          start-placeholder="开始日期"
                          end-placeholder="结束日期"
                          style="width: 100%"
                          @change="calculateDays" />
        </el-form-item>
                 <el-form-item label="开始时间" prop="startTime" v-if="currentType === 'overtime'">
           <el-time-picker
             v-model="form.startTime"
             placeholder="开始时间"
             format="HH:mm"
             value-format="HH:mm"
             style="width: 100%"
             @change="validateTimeField('startTime')"
           />
         </el-form-item>
         <el-form-item label="结束时间" prop="endTime" v-if="currentType === 'overtime'">
           <el-time-picker
             v-model="form.endTime"
             placeholder="结束时间"
             format="HH:mm"
             value-format="HH:mm"
             style="width: 100%"
             @change="validateTimeField('endTime')"
           />
         </el-form-item>
        <el-form-item label="倍率" prop="rate" v-if="currentType === 'overtime'">
          <el-input-number v-model="form.rate" :min="1" :max="3" :step="0.5" style="width: 100%" />
        <el-form-item label="天数"
                      prop="days"
                      v-if="currentType === 'holiday'">
          <el-input-number v-model="form.days"
                           :min="0"
                           style="width: 100%" />
        </el-form-item>
                 <el-form-item label="上班时间" prop="workStartTime" v-if="currentType === 'worktime'">
           <el-time-picker
             v-model="form.workStartTime"
             placeholder="上班时间"
             format="HH:mm"
             value-format="HH:mm"
             style="width: 100%"
             @change="validateTimeField('workStartTime')"
           />
         </el-form-item>
         <el-form-item label="下班时间" prop="workEndTime" v-if="currentType === 'worktime'">
           <el-time-picker
             v-model="form.workEndTime"
             placeholder="下班时间"
             format="HH:mm"
             value-format="HH:mm"
             style="width: 100%"
             @change="validateTimeField('workEndTime')"
           />
         </el-form-item>
        <el-form-item label="弹性上班" prop="flexibleStart" v-if="currentType === 'worktime'">
        <el-form-item label="开始时间"
                      prop="startTime"
                      v-if="currentType === 'overtime'">
          <el-time-picker v-model="form.startTime"
                          placeholder="开始时间"
                          format="HH:mm"
                          value-format="HH:mm"
                          style="width: 100%"
                          @change="validateTimeField('startTime')" />
        </el-form-item>
        <el-form-item label="结束时间"
                      prop="endTime"
                      v-if="currentType === 'overtime'">
          <el-time-picker v-model="form.endTime"
                          placeholder="结束时间"
                          format="HH:mm"
                          value-format="HH:mm"
                          style="width: 100%"
                          @change="validateTimeField('endTime')" />
        </el-form-item>
        <el-form-item label="倍率"
                      prop="rate"
                      v-if="currentType === 'overtime'">
          <el-input-number v-model="form.rate"
                           :min="1"
                           :max="3"
                           :step="0.5"
                           style="width: 100%" />
        </el-form-item>
        <el-form-item label="上班时间"
                      prop="workStartTime"
                      v-if="currentType === 'worktime'">
          <el-time-picker v-model="form.workStartTime"
                          placeholder="上班时间"
                          format="HH:mm"
                          value-format="HH:mm"
                          style="width: 100%"
                          @change="validateTimeField('workStartTime')" />
        </el-form-item>
        <el-form-item label="下班时间"
                      prop="workEndTime"
                      v-if="currentType === 'worktime'">
          <el-time-picker v-model="form.workEndTime"
                          placeholder="下班时间"
                          format="HH:mm"
                          value-format="HH:mm"
                          style="width: 100%"
                          @change="validateTimeField('workEndTime')" />
        </el-form-item>
        <el-form-item label="弹性上班"
                      prop="flexibleStart"
                      v-if="currentType === 'worktime'">
          <el-switch v-model="form.flexibleStart" />
        </el-form-item>
        <el-form-item label="弹性时间(分钟)" prop="flexibleMinutes" v-if="currentType === 'worktime' && form.flexibleStart">
          <el-input-number v-model="form.flexibleMinutes" :min="0" :max="120" style="width: 100%" />
        <el-form-item label="弹性时间(分钟)"
                      prop="flexibleMinutes"
                      v-if="currentType === 'worktime' && form.flexibleStart">
          <el-input-number v-model="form.flexibleMinutes"
                           :min="0"
                           :max="120"
                           style="width: 100%" />
        </el-form-item>
        <el-form-item label="状态" prop="status">
           <el-radio-group v-model="form.status">
             <el-radio value="active">启用</el-radio>
             <el-radio value="inactive">停用</el-radio>
           </el-radio-group>
         </el-form-item>
        <el-form-item label="状态"
                      prop="status">
          <el-radio-group v-model="form.status">
            <el-radio value="active">启用</el-radio>
            <el-radio value="inactive">停用</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="submitForm">确定</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="submitForm">确定</el-button>
        </span>
      </template>
    </el-dialog>
@@ -312,933 +421,1001 @@
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { listHolidaySettings, addHolidaySettings, updateHolidaySettings, delHolidaySettings, listAnnualLeaveSettingList, addAnnualLeaveSetting, updateAnnualLeaveSetting, delAnnualLeaveSetting, listOvertimeSettingList, addOvertimeSetting, updateOvertimeSetting, delOvertimeSetting, listWorkingHoursSettingList, addWorkingHoursSetting, updateWorkingHoursSetting, delWorkingHoursSetting } from '@/api/collaborativeApproval/attendanceManagement.js'
  import { ref, reactive, onMounted, onUnmounted } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    listHolidaySettings,
    addHolidaySettings,
    updateHolidaySettings,
    delHolidaySettings,
    listAnnualLeaveSettingList,
    addAnnualLeaveSetting,
    updateAnnualLeaveSetting,
    delAnnualLeaveSetting,
    listOvertimeSettingList,
    addOvertimeSetting,
    updateOvertimeSetting,
    delOvertimeSetting,
    listWorkingHoursSettingList,
    addWorkingHoursSetting,
    updateWorkingHoursSetting,
    delWorkingHoursSetting,
  } from "@/api/collaborativeApproval/attendanceManagement.js";
// 当前激活的标签页
const activeTab = ref('holiday')
  // 当前激活的标签页
  const activeTab = ref("holiday");
// 弹窗相关
const dialogVisible = ref(false)
const dialogTitle = ref('')
const currentType = ref('')
const currentAction = ref('')
const currentEditId = ref('')
const formRef = ref()
const page = {
  // 弹窗相关
  const dialogVisible = ref(false);
  const dialogTitle = ref("");
  const currentType = ref("");
  const currentAction = ref("");
  const currentEditId = ref("");
  const formRef = ref();
  const page = {
    current: 1,
    size: 20,
    total: 0,
  }
const holidayData = ref([])
const annualData = ref([])
const overtimeData = ref([])
const worktimeData = ref([])
  };
  const holidayData = ref([]);
  const annualData = ref([]);
  const overtimeData = ref([]);
  const worktimeData = ref([]);
// 打卡记录相关数据
const attendanceData = ref([])
const filteredAttendanceData = ref([])
const attendanceDate = ref('')
const attendanceStatus = ref('')
  // 打卡记录相关数据
  const attendanceData = ref([]);
  const filteredAttendanceData = ref([]);
  const attendanceDate = ref("");
  const attendanceStatus = ref("");
// 表单数据
const form = reactive({
  name: '',
  type: '',
  dateRange: [],
  startDate: '',
  endDate: '',
  days: 0,
  employeeType: '',
  workYears: '',
  annualDays: 0,
  maxCarryOver: 0,
  startTime: '', // 加班开始时间
  endTime: '',   // 加班结束时间
  workStartTime: '', // 上班时间
  workEndTime: '',   // 下班时间
  rate: 1.5,
  flexibleStart: false,
  flexibleMinutes: 30,
  status: 'active'
})
// 表单验证规则
const rules = {
  name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
  type: [{ required: true, message: '请选择类型', trigger: 'change' }],
  dateRange: [{ required: true, message: '请选择日期范围', trigger: 'change' }],
  days: [{ required: true, message: '请输入天数', trigger: 'blur' }],
  employeeType: [{ required: true, message: '请选择员工类型', trigger: 'change' }],
  workYears: [{ required: true, message: '请输入工作年限', trigger: 'blur' }],
  annualDays: [{ required: true, message: '请输入年假天数', trigger: 'blur' }],
  maxCarryOver: [{ required: true, message: '请输入最大结转天数', trigger: 'blur' }],
  startTime: [{
    required: true,
    message: '请选择开始时间',
    trigger: 'change',
    validator: (rule, value, callback) => {
      if (!value) {
        callback(new Error('请选择开始时间'))
      } else {
        callback()
      }
    }
  }],
  endTime: [{
    required: true,
    message: '请选择结束时间',
    trigger: 'change',
    validator: (rule, value, callback) => {
      if (!value) {
        callback(new Error('请选择结束时间'))
      } else {
        callback()
      }
    }
  }],
  workStartTime: [{
    required: true,
    message: '请选择上班时间',
    trigger: 'change',
    validator: (rule, value, callback) => {
      if (!value) {
        callback(new Error('请选择上班时间'))
      } else {
        callback()
      }
    }
  }],
  workEndTime: [{
    required: true,
    message: '请选择下班时间',
    trigger: 'change',
    validator: (rule, value, callback) => {
      if (!value) {
        callback(new Error('请选择下班时间'))
      } else {
        callback()
      }
    }
  }],
  rate: [{ required: true, message: '请输入倍率', trigger: 'blur' }]
}
// 工具函数
const getTagType = (type) => {
  const tagMap = {
    legal: 'success', adjustment: 'warning', special: 'info', company: 'primary',
    weekday: 'primary', weekend: 'warning', holiday: 'danger', night: 'info',
    regular: 'success', probation: 'info', intern: 'danger'
  }
  return tagMap[type] || 'info'
}
const getTypeLabel = (type) => {
  const labelMap = {
    legal: '法定节假日', adjustment: '调休日', special: '特殊假期', company: '公司假期',
    weekday: '工作日加班', weekend: '周末加班', holiday: '节假日加班', night: '深夜加班',
    regular: '正式员工', probation: '试用期员工', intern: '实习生'
  }
  return labelMap[type] || type
}
// 打卡记录相关工具函数
const getAttendanceTagType = (status) => {
  const tagMap = {
    normal: 'success',
    late: 'warning',
    early: 'warning',
    absent: 'danger'
  }
  return tagMap[status] || 'info'
}
const getAttendanceStatusLabel = (status) => {
  const labelMap = {
    normal: '正常',
    late: '迟到',
    early: '早退',
    absent: '缺勤'
  }
  return labelMap[status] || status
}
const getTypeOptions = () => {
  if (currentType.value === 'holiday') {
    return [
      { label: '法定节假日', value: 'legal' },
      { label: '调休日', value: 'adjustment' },
      { label: '特殊假期', value: 'special' },
      { label: '公司假期', value: 'company' }
    ]
  } else if (currentType.value === 'overtime') {
    return [
      { label: '工作日加班', value: 'weekday' },
      { label: '周末加班', value: 'weekend' },
      { label: '节假日加班', value: 'holiday' },
      { label: '深夜加班', value: 'night' }
    ]
  } else if (currentType.value === 'annual') {
    return [
      { label: '正式员工', value: 'regular' },
      { label: '试用期员工', value: 'probation' },
      { label: '实习生', value: 'intern' }
    ]
  }
  return []
}
// 计算假期天数
const calculateDays = () => {
  try {
    if (form.dateRange && form.dateRange.length === 2 && form.dateRange[0] && form.dateRange[1]) {
      const start = new Date(form.dateRange[0])
      const end = new Date(form.dateRange[1])
      form.startDate = start.toISOString().split('T')[0]
      form.endDate = end.toISOString().split('T')[0]
      if (isNaN(start.getTime()) || isNaN(end.getTime())) {
        console.warn('无效的日期格式')
        return
      }
      const diffTime = Math.abs(end - start)
      const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1
      form.days = diffDays
    }
  } catch (error) {
    console.error('计算天数失败:', error)
  }
}
// 验证时间格式
// const validateTime = (time) => {
//   if (!time) return ''
//   if (typeof time === 'string') return time
//   if (time instanceof Date) {
//     return time.toTimeString().slice(0, 5)
//   }
//   return ''
// }
// 验证时间字段
const validateTimeField = (fieldName) => {
  try {
    const value = form[fieldName]
    if (value && typeof value === 'object' && value.hour !== undefined) {
      // 如果是时间对象,转换为字符串格式
      const hours = value.hour.toString().padStart(2, '0')
      const minutes = value.minute.toString().padStart(2, '0')
      form[fieldName] = `${hours}:${minutes}`
    }
  } catch (error) {
    console.error(`验证时间字段 ${fieldName} 失败:`, error)
    form[fieldName] = ''
  }
}
// 打开弹窗
const openDialog = (type, action, row = null) => {
  try {
    currentType.value = type
    currentAction.value = action
    if (action === 'add') {
      dialogTitle.value = `新增${getTypeName(type)}`
      currentEditId.value = ''
      resetForm()
    } else if (action === 'edit' && row) {
      dialogTitle.value = `编辑${getTypeName(type)}`
      currentEditId.value = row.id
      fillForm(row)
    }
    dialogVisible.value = true
  } catch (error) {
    console.error('打开弹窗失败:', error)
    ElMessage.error('打开弹窗失败,请重试')
  }
}
const getTypeName = (type) => {
  const nameMap = {
    holiday: '假期',
    annual: '年假规则',
    overtime: '加班规则',
    worktime: '时间段'
  }
  return nameMap[type] || ''
}
const resetForm = () => {
  Object.assign(form, {
    name: '',
    type: '',
  // 表单数据
  const form = reactive({
    name: "",
    type: "",
    dateRange: [],
    startDate: '',
    endDate: '',
    startDate: "",
    endDate: "",
    days: 0,
    employeeType: '',
    workYears: '',
    employeeType: "",
    workYears: "",
    annualDays: 0,
    maxCarryOver: 0,
    startTime: '',
    endTime: '',
    workStartTime: '',
    workEndTime: '',
    startTime: "", // 加班开始时间
    endTime: "", // 加班结束时间
    workStartTime: "", // 上班时间
    workEndTime: "", // 下班时间
    rate: 1.5,
    flexibleStart: false,
    flexibleMinutes: 30,
    status: 'active'
  })
}
    status: "active",
  });
const fillForm = (row) => {
  if (currentType.value === 'holiday') {
    Object.assign(form, {
      name: row.name,
      type: row.type,
      dateRange: [new Date(row.startDate), new Date(row.endDate)],
      startDate: row.startDate,
      endDate: row.endDate,
      days: row.days,
      status: row.status
    })
  } else if (currentType.value === 'annual') {
    Object.assign(form, {
      employeeType: row.employeeType,
      workYears: row.workYears,
      annualDays: row.annualDays,
      maxCarryOver: row.maxCarryOver,
      status: row.status
    })
  } else if (currentType.value === 'overtime') {
    Object.assign(form, {
      name: row.name,
      type: row.type,
      startTime: row.startTime || '',
      endTime: row.endTime || '',
      rate: row.rate,
      status: row.status
    })
  } else if (currentType.value === 'worktime') {
    Object.assign(form, {
      name: row.name,
      workStartTime: row.startTime || '',
      workEndTime: row.endTime || '',
      flexibleStart: row.flexibleStart,
      flexibleMinutes: row.flexibleMinutes,
      status: row.status
    })
  }
}
  // 表单验证规则
  const rules = {
    name: [{ required: true, message: "请输入名称", trigger: "blur" }],
    type: [{ required: true, message: "请选择类型", trigger: "change" }],
    dateRange: [{ required: true, message: "请选择日期范围", trigger: "change" }],
    days: [{ required: true, message: "请输入天数", trigger: "blur" }],
    employeeType: [
      { required: true, message: "请选择员工类型", trigger: "change" },
    ],
    workYears: [{ required: true, message: "请输入工作年限", trigger: "blur" }],
    annualDays: [{ required: true, message: "请输入年假天数", trigger: "blur" }],
    maxCarryOver: [
      { required: true, message: "请输入最大结转天数", trigger: "blur" },
    ],
    startTime: [
      {
        required: true,
        message: "请选择开始时间",
        trigger: "change",
        validator: (rule, value, callback) => {
          if (!value) {
            callback(new Error("请选择开始时间"));
          } else {
            callback();
          }
        },
      },
    ],
    endTime: [
      {
        required: true,
        message: "请选择结束时间",
        trigger: "change",
        validator: (rule, value, callback) => {
          if (!value) {
            callback(new Error("请选择结束时间"));
          } else {
            callback();
          }
        },
      },
    ],
    workStartTime: [
      {
        required: true,
        message: "请选择上班时间",
        trigger: "change",
        validator: (rule, value, callback) => {
          if (!value) {
            callback(new Error("请选择上班时间"));
          } else {
            callback();
          }
        },
      },
    ],
    workEndTime: [
      {
        required: true,
        message: "请选择下班时间",
        trigger: "change",
        validator: (rule, value, callback) => {
          if (!value) {
            callback(new Error("请选择下班时间"));
          } else {
            callback();
          }
        },
      },
    ],
    rate: [{ required: true, message: "请输入倍率", trigger: "blur" }],
  };
  // 工具函数
  const getTagType = type => {
    const tagMap = {
      legal: "success",
      adjustment: "warning",
      special: "info",
      company: "primary",
      weekday: "primary",
      weekend: "warning",
      holiday: "danger",
      night: "info",
      regular: "success",
      probation: "info",
      intern: "danger",
    };
    return tagMap[type] || "info";
  };
// 提交表单
const submitForm = async () => {
  try {
    if (!formRef.value) {
      ElMessage.error('表单引用不存在')
      return
  const getTypeLabel = type => {
    const labelMap = {
      legal: "法定节假日",
      adjustment: "调休日",
      special: "特殊假期",
      company: "公司假期",
      weekday: "工作日加班",
      weekend: "周末加班",
      holiday: "节假日加班",
      night: "深夜加班",
      regular: "正式员工",
      probation: "试用期员工",
      intern: "实习生",
    };
    return labelMap[type] || type;
  };
  // 打卡记录相关工具函数
  const getAttendanceTagType = status => {
    const tagMap = {
      normal: "success",
      late: "warning",
      early: "warning",
      absent: "danger",
    };
    return tagMap[status] || "info";
  };
  const getAttendanceStatusLabel = status => {
    const labelMap = {
      normal: "正常",
      late: "迟到",
      early: "早退",
      absent: "缺勤",
    };
    return labelMap[status] || status;
  };
  const getTypeOptions = () => {
    if (currentType.value === "holiday") {
      return [
        { label: "法定节假日", value: "legal" },
        { label: "调休日", value: "adjustment" },
        { label: "特殊假期", value: "special" },
        { label: "公司假期", value: "company" },
      ];
    } else if (currentType.value === "overtime") {
      return [
        { label: "工作日加班", value: "weekday" },
        { label: "周末加班", value: "weekend" },
        { label: "节假日加班", value: "holiday" },
        { label: "深夜加班", value: "night" },
      ];
    } else if (currentType.value === "annual") {
      return [
        { label: "正式员工", value: "regular" },
        { label: "试用期员工", value: "probation" },
        { label: "实习生", value: "intern" },
      ];
    }
    return [];
  };
    await formRef.value.validate()
  // 计算假期天数
  const calculateDays = () => {
    try {
      if (
        form.dateRange &&
        form.dateRange.length === 2 &&
        form.dateRange[0] &&
        form.dateRange[1]
      ) {
        const start = new Date(form.dateRange[0]);
        const end = new Date(form.dateRange[1]);
        form.startDate = start.toISOString().split("T")[0];
        form.endDate = end.toISOString().split("T")[0];
    if (currentAction.value === 'add') {
      addItem()
    } else if (currentAction.value === 'edit') {
      editItem()
    }
    dialogVisible.value = false
    ElMessage.success('操作成功')
  } catch (error) {
    console.error('表单验证失败:', error)
    ElMessage.error('表单验证失败,请检查输入')
  }
}
const addItem = () => {
  if (currentType.value === 'holiday') {
    const params = {
      name: form.name,
      type: form.type,
      startDate: form.startDate,
      endDate: form.endDate,
      days: form.days,
      status: form.status
    }
    addHolidaySettings(params).then(res => {
      if(res.code == 200){
        ElMessage.success("添加成功");
        // dialogVisible.value = false;
        getHolidaySettingsList()
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
  } else if (currentType.value === 'annual') {
    // annualData.value.push(newItem)
    const params = {
      employeeType: form.employeeType,
      workYears: form.workYears,
      annualDays: form.annualDays,
      maxCarryOver: form.maxCarryOver,
      status: form.status
    }
    addAnnualLeaveSetting(params).then(res => {
      if(res.code == 200){
        ElMessage.success("添加成功");
        // dialogVisible.value = false;
        getAnnualLeaveSettingList()
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
  } else if (currentType.value === 'overtime') {
    const params = {
      name: form.name,
      type: form.type,
      startTime: form.startTime || '',
      endTime: form.endTime || '',
      rate: form.rate,
      status: form.status
    }
    addOvertimeSetting(params).then(res => {
      if(res.code == 200){
        ElMessage.success("添加成功");
        // dialogVisible.value = false;
        getOvertimeSettingList()
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
    // newItem.startTime = form.startTime || ''
    // newItem.endTime = form.endTime || ''
    // overtimeData.value.push(newItem)
  } else if (currentType.value === 'worktime') {
    const params = {
      name: form.name,
      startTime: form.workStartTime || '',
      endTime: form.workEndTime || '',
      flexibleStart: form.flexibleStart,
      flexibleMinutes: form.flexibleMinutes,
      status: form.status
    }
    addWorkingHoursSetting(params).then(res => {
      if(res.code == 200){
        ElMessage.success("添加成功");
        getWorkingHoursSettingList()
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
    // newItem.startTime = form.workStartTime || ''
    // newItem.endTime = form.workEndTime || ''
    // worktimeData.value.push(newItem)
  }
}
const editItem = () => {
  let dataArray
  let index
  if (currentType.value === 'holiday') {
    const params = {
      id: currentEditId.value,
      name: form.name,
      type: form.type,
      startDate: form.dateRange[0].toISOString().split('T')[0],
      endDate: form.dateRange[1].toISOString().split('T')[0],
      days: form.days,
      status: form.status
    }
    updateHolidaySettings(params).then(res => {
      if(res.code == 200){
        ElMessage.success("更新成功");
        // dialogVisible.value = false;
        getHolidaySettingsList()
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
  } else if (currentType.value === 'annual') {
    const params = {
      id: currentEditId.value,
      employeeType: form.employeeType,
      workYears: form.workYears,
      annualDays: form.annualDays,
      maxCarryOver: form.maxCarryOver,
      status: form.status
    }
    updateAnnualLeaveSetting(params).then(res => {
      if(res.code == 200){
        ElMessage.success("更新成功");
        getAnnualLeaveSettingList()
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
  } else if (currentType.value === 'overtime') {
    const params = {
      id: currentEditId.value,
      name: form.name,
      type: form.type,
      startTime: form.startTime || '',
      endTime: form.endTime || '',
      rate: form.rate,
      status: form.status
    }
    updateOvertimeSetting(params).then(res => {
      if(res.code == 200){
        ElMessage.success("更新成功");
        getOvertimeSettingList()
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
    // dataArray = overtimeData.value
    // index = dataArray.findIndex(item => item.id === currentEditId.value)
    // if (index > -1) {
    //   dataArray[index] = {
    //     ...dataArray[index],
    //     name: form.name,
    //     type: form.type,
    //     startTime: form.startTime || '',
    //     endTime: form.endTime || '',
    //     rate: form.rate,
    //     status: form.status
    //   }
    // }
  } else if (currentType.value === 'worktime') {
    const params = {
      id: currentEditId.value,
      name: form.name,
      startTime: form.workStartTime || '',
      endTime: form.workEndTime || '',
      flexibleStart: form.flexibleStart,
      flexibleMinutes: form.flexibleMinutes,
      status: form.status
    }
    updateWorkingHoursSetting(params).then(res => {
      if(res.code == 200){
        ElMessage.success("更新成功");
        getWorkingHoursSettingList()
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
    // dataArray = worktimeData.value
    // index = dataArray.findIndex(item => item.id === currentEditId.value)
    // if (index > -1) {
    //   dataArray[index] = {
    //     ...dataArray[index],
    //     name: form.name,
    //     startTime: form.workStartTime || '',
    //     endTime: form.workEndTime || '',
    //     flexibleStart: form.flexibleStart,
    //     flexibleMinutes: form.flexibleMinutes,
    //     status: form.status
    //   }
    // }
  }
}
// 打卡记录过滤功能
const filterAttendanceData = () => {
  let filtered = attendanceData.value
  // 按日期过滤
  if (attendanceDate.value) {
    filtered = filtered.filter(item => item.date === attendanceDate.value)
  }
  // 按状态过滤
  if (attendanceStatus.value) {
    filtered = filtered.filter(item => item.status === attendanceStatus.value)
  }
  filteredAttendanceData.value = filtered
}
// 导出打卡记录
const exportAttendance = () => {
  ElMessage.success('导出功能开发中...')
}
// 初始化打卡记录假数据
const initAttendanceData = () => {
  const mockData = [
    {
      id: 1,
      employeeName: '陈志强',
      department: '技术部',
      date: '2025-08-15',
      clockInTime: '09:00:00',
      clockOutTime: '18:00:00',
      workHours: '8.0h',
      status: 'normal',
      location: '公司总部',
      remark: ''
    },
    {
      id: 2,
      employeeName: '李雪梅',
      department: '市场部',
      date: '2025-08-16',
       clockInTime: '08:58:00',
       clockOutTime: '18:05:00',
       workHours: '8.12h',
       status: 'normal',
       location: '公司总部',
       remark: ''
     },
     {
       id: 3,
       employeeName: '王建华',
       department: '人事部',
       date: '2025-08-16',
       clockInTime: '09:02:00',
       clockOutTime: '18:00:00',
       workHours: '7.97h',
       status: 'normal',
       location: '公司总部',
       remark: ''
     },
     {
       id: 4,
       employeeName: '赵晓丽',
       department: '财务部',
       date: '2025-09-02',
       clockInTime: '08:55:00',
       clockOutTime: '18:10:00',
       workHours: '8.25h',
       status: 'normal',
       location: '公司总部',
       remark: ''
     },
     {
       id: 5,
       employeeName: '张国庆',
       department: '技术部',
       date: '2025-09-02',
       clockInTime: '09:00:00',
       clockOutTime: '18:30:00',
       workHours: '8.5h',
       status: 'normal',
       location: '公司总部',
       remark: '加班'
     },
     {
       id: 6,
       employeeName: '刘明辉',
       department: '运营部',
       date: '2025-09-03',
       clockInTime: '09:05:00',
       clockOutTime: '18:00:00',
       workHours: '7.92h',
       status: 'normal',
       location: '公司总部',
       remark: ''
     },
     {
       id: 7,
       employeeName: '孙丽华',
       department: '设计部',
       date: '2025-09-03',
       clockInTime: '08:59:00',
       clockOutTime: '18:02:00',
       workHours: '8.05h',
       status: 'normal',
       location: '公司总部',
       remark: ''
     },
     {
       id: 8,
       employeeName: '周建军',
       department: '销售部',
       date: '2025-09-04',
       clockInTime: '09:15:00',
       clockOutTime: '18:00:00',
       workHours: '7.75h',
       status: 'late',
       location: '公司总部',
       remark: '交通堵塞'
     },
     {
       id: 9,
       employeeName: '吴小芳',
       department: '客服部',
       date: '2025-09-04',
       clockInTime: '09:01:00',
       clockOutTime: '18:00:00',
       workHours: '7.98h',
       status: 'normal',
       location: '公司总部',
       remark: ''
     },
     {
       id: 10,
       employeeName: '马文杰',
       department: '技术部',
       date: '2025-09-05',
       clockInTime: '08:57:00',
       clockOutTime: '17:30:00',
       workHours: '7.55h',
       status: 'early',
       location: '公司总部',
       remark: '有急事提前离开'
     },
     {
       id: 11,
       employeeName: '林晓东',
       department: '行政部',
       date: '2025-09-05',
       clockInTime: '09:03:00',
       clockOutTime: '18:08:00',
       workHours: '8.08h',
       status: 'normal',
       location: '公司总部',
       remark: ''
     },
     {
       id: 12,
       employeeName: '黄美玲',
       department: '财务部',
       date: '2025-09-06',
       clockInTime: '',
       clockOutTime: '',
       workHours: '0h',
       status: 'absent',
       location: '',
       remark: '请病假'
     },
    {
      id: 13,
      employeeName: '郑海涛',
      department: '市场部',
      date: '2025-08-14',
      clockInTime: '09:00:00',
      clockOutTime: '18:00:00',
      workHours: '8.0h',
      status: 'normal',
      location: '公司总部',
      remark: ''
    },
    {
      id: 14,
      employeeName: '谢丽娟',
      department: '人事部',
      date: '2025-08-20',
      clockInTime: '08:58:00',
      clockOutTime: '18:03:00',
      workHours: '8.08h',
      status: 'normal',
      location: '公司总部',
      remark: ''
    },
    {
      id: 15,
      employeeName: '何志伟',
      department: '技术部',
      date: '2025-08-21',
      clockInTime: '09:10:00',
      clockOutTime: '18:00:00',
      workHours: '7.83h',
      status: 'late',
      location: '公司总部',
      remark: ''
    },
    {
      id: 16,
      employeeName: '许雅芳',
      department: '设计部',
      date: '2025-08-22',
      clockInTime: '09:01:00',
      clockOutTime: '18:00:00',
      workHours: '7.98h',
      status: 'normal',
      location: '公司总部',
      remark: ''
    },
    {
      id: 17,
      employeeName: '邓建平',
      department: '运营部',
      date: '2025-09-10',
      clockInTime: '08:59:00',
      clockOutTime: '18:05:00',
      workHours: '8.1h',
      status: 'normal',
      location: '公司总部',
      remark: ''
    },
    {
      id: 18,
      employeeName: '曾小红',
      department: '客服部',
      date: '2025-09-11',
      clockInTime: '09:02:00',
      clockOutTime: '18:00:00',
      workHours: '7.97h',
      status: 'normal',
      location: '公司总部',
      remark: ''
    }
  ]
  attendanceData.value = mockData
  filteredAttendanceData.value = mockData
}
// 删除项目
const deleteItem = (type, row) => {
  ElMessageBox.confirm('确定要删除这个项目吗?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    let ids = [];
    let dataArray
    if (type === 'holiday') {
      ids.push(row.id)
      delHolidaySettings(ids).then(res => {
        if(res.code == 200){
          ElMessage.success("删除成功");
          ids = []
          getHolidaySettingsList()
        if (isNaN(start.getTime()) || isNaN(end.getTime())) {
          console.warn("无效的日期格式");
          return;
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
        const diffTime = Math.abs(end - start);
        const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1;
        form.days = diffDays;
      }
    } catch (error) {
      console.error("计算天数失败:", error);
    }
    else if (type === 'annual') {
      ids.push(row.id)
      delAnnualLeaveSetting(ids).then(res => {
        if(res.code == 200){
          ElMessage.success("删除成功");
          ids = []
          getAnnualLeaveSettingList()
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
  };
  // 验证时间格式
  // const validateTime = (time) => {
  //   if (!time) return ''
  //   if (typeof time === 'string') return time
  //   if (time instanceof Date) {
  //     return time.toTimeString().slice(0, 5)
  //   }
  //   return ''
  // }
  // 验证时间字段
  const validateTimeField = fieldName => {
    try {
      const value = form[fieldName];
      if (value && typeof value === "object" && value.hour !== undefined) {
        // 如果是时间对象,转换为字符串格式
        const hours = value.hour.toString().padStart(2, "0");
        const minutes = value.minute.toString().padStart(2, "0");
        form[fieldName] = `${hours}:${minutes}`;
      }
    } catch (error) {
      console.error(`验证时间字段 ${fieldName} 失败:`, error);
      form[fieldName] = "";
    }
    else if (type === 'overtime') {
      ids.push(row.id)
      delOvertimeSetting(ids).then(res => {
        if(res.code == 200){
          ElMessage.success("删除成功");
          ids = []
          getOvertimeSettingList()
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
  };
  // 打开弹窗
  const openDialog = (type, action, row = null) => {
    try {
      currentType.value = type;
      currentAction.value = action;
      if (action === "add") {
        dialogTitle.value = `新增${getTypeName(type)}`;
        currentEditId.value = "";
        resetForm();
      } else if (action === "edit" && row) {
        dialogTitle.value = `编辑${getTypeName(type)}`;
        currentEditId.value = row.id;
        fillForm(row);
      }
      dialogVisible.value = true;
    } catch (error) {
      console.error("打开弹窗失败:", error);
      ElMessage.error("打开弹窗失败,请重试");
    }
    else if (type === 'worktime') {
      ids.push(row.id)
      delWorkingHoursSetting(ids).then(res => {
        if(res.code == 200){
          ElMessage.success("删除成功");
          ids = []
          getWorkingHoursSettingList()
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
  };
  const getTypeName = type => {
    const nameMap = {
      holiday: "假期",
      annual: "年假规则",
      overtime: "加班规则",
      worktime: "时间段",
    };
    return nameMap[type] || "";
  };
  const resetForm = () => {
    Object.assign(form, {
      name: "",
      type: "",
      dateRange: [],
      startDate: "",
      endDate: "",
      days: 0,
      employeeType: "",
      workYears: "",
      annualDays: 0,
      maxCarryOver: 0,
      startTime: "",
      endTime: "",
      workStartTime: "",
      workEndTime: "",
      rate: 1.5,
      flexibleStart: false,
      flexibleMinutes: 30,
      status: "active",
    });
  };
  const fillForm = row => {
    if (currentType.value === "holiday") {
      Object.assign(form, {
        name: row.name,
        type: row.type,
        dateRange: [new Date(row.startDate), new Date(row.endDate)],
        startDate: row.startDate,
        endDate: row.endDate,
        days: row.days,
        status: row.status,
      });
    } else if (currentType.value === "annual") {
      Object.assign(form, {
        employeeType: row.employeeType,
        workYears: row.workYears,
        annualDays: row.annualDays,
        maxCarryOver: row.maxCarryOver,
        status: row.status,
      });
    } else if (currentType.value === "overtime") {
      Object.assign(form, {
        name: row.name,
        type: row.type,
        startTime: row.startTime || "",
        endTime: row.endTime || "",
        rate: row.rate,
        status: row.status,
      });
    } else if (currentType.value === "worktime") {
      Object.assign(form, {
        name: row.name,
        workStartTime: row.startTime || "",
        workEndTime: row.endTime || "",
        flexibleStart: row.flexibleStart,
        flexibleMinutes: row.flexibleMinutes,
        status: row.status,
      });
    }
  };
  // 提交表单
  const submitForm = async () => {
    try {
      if (!formRef.value) {
        ElMessage.error("表单引用不存在");
        return;
      }
      await formRef.value.validate();
      if (currentAction.value === "add") {
        addItem();
      } else if (currentAction.value === "edit") {
        editItem();
      }
      dialogVisible.value = false;
      ElMessage.success("操作成功");
    } catch (error) {
      console.error("表单验证失败:", error);
      ElMessage.error("表单验证失败,请检查输入");
    }
  };
  const addItem = () => {
    if (currentType.value === "holiday") {
      const params = {
        name: form.name,
        type: form.type,
        startDate: form.startDate,
        endDate: form.endDate,
        days: form.days,
        status: form.status,
      };
      addHolidaySettings(params)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("添加成功");
            // dialogVisible.value = false;
            getHolidaySettingsList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
    } else if (currentType.value === "annual") {
      // annualData.value.push(newItem)
      const params = {
        employeeType: form.employeeType,
        workYears: form.workYears,
        annualDays: form.annualDays,
        maxCarryOver: form.maxCarryOver,
        status: form.status,
      };
      addAnnualLeaveSetting(params)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("添加成功");
            // dialogVisible.value = false;
            getAnnualLeaveSettingList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
    } else if (currentType.value === "overtime") {
      const params = {
        name: form.name,
        type: form.type,
        startTime: form.startTime || "",
        endTime: form.endTime || "",
        rate: form.rate,
        status: form.status,
      };
      addOvertimeSetting(params)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("添加成功");
            // dialogVisible.value = false;
            getOvertimeSettingList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
      // newItem.startTime = form.startTime || ''
      // newItem.endTime = form.endTime || ''
      // overtimeData.value.push(newItem)
    } else if (currentType.value === "worktime") {
      const params = {
        name: form.name,
        startTime: form.workStartTime || "",
        endTime: form.workEndTime || "",
        flexibleStart: form.flexibleStart,
        flexibleMinutes: form.flexibleMinutes,
        status: form.status,
      };
      addWorkingHoursSetting(params)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("添加成功");
            getWorkingHoursSettingList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
      // newItem.startTime = form.workStartTime || ''
      // newItem.endTime = form.workEndTime || ''
      // worktimeData.value.push(newItem)
    }
  };
  const editItem = () => {
    let dataArray;
    let index;
    if (currentType.value === "holiday") {
      const params = {
        id: currentEditId.value,
        name: form.name,
        type: form.type,
        startDate: form.dateRange[0].toISOString().split("T")[0],
        endDate: form.dateRange[1].toISOString().split("T")[0],
        days: form.days,
        status: form.status,
      };
      updateHolidaySettings(params)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("更新成功");
            // dialogVisible.value = false;
            getHolidaySettingsList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
    } else if (currentType.value === "annual") {
      const params = {
        id: currentEditId.value,
        employeeType: form.employeeType,
        workYears: form.workYears,
        annualDays: form.annualDays,
        maxCarryOver: form.maxCarryOver,
        status: form.status,
      };
      updateAnnualLeaveSetting(params)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("更新成功");
            getAnnualLeaveSettingList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
    } else if (currentType.value === "overtime") {
      const params = {
        id: currentEditId.value,
        name: form.name,
        type: form.type,
        startTime: form.startTime || "",
        endTime: form.endTime || "",
        rate: form.rate,
        status: form.status,
      };
      updateOvertimeSetting(params)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("更新成功");
            getOvertimeSettingList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
      // dataArray = overtimeData.value
      // index = dataArray.findIndex(item => item.id === currentEditId.value)
      // if (index > -1) {
      //   dataArray[index] = {
      //     ...dataArray[index],
      //     name: form.name,
      //     type: form.type,
      //     startTime: form.startTime || '',
      //     endTime: form.endTime || '',
      //     rate: form.rate,
      //     status: form.status
      //   }
      // }
    } else if (currentType.value === "worktime") {
      const params = {
        id: currentEditId.value,
        name: form.name,
        startTime: form.workStartTime || "",
        endTime: form.workEndTime || "",
        flexibleStart: form.flexibleStart,
        flexibleMinutes: form.flexibleMinutes,
        status: form.status,
      };
      updateWorkingHoursSetting(params)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("更新成功");
            getWorkingHoursSettingList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
      // dataArray = worktimeData.value
      // index = dataArray.findIndex(item => item.id === currentEditId.value)
      // if (index > -1) {
      //   dataArray[index] = {
      //     ...dataArray[index],
      //     name: form.name,
      //     startTime: form.workStartTime || '',
      //     endTime: form.workEndTime || '',
      //     flexibleStart: form.flexibleStart,
      //     flexibleMinutes: form.flexibleMinutes,
      //     status: form.status
      //   }
      // }
    }
  };
  // 打卡记录过滤功能
  const filterAttendanceData = () => {
    let filtered = attendanceData.value;
    // 按日期过滤
    if (attendanceDate.value) {
      filtered = filtered.filter(item => item.date === attendanceDate.value);
    }
    // const index = dataArray.findIndex(item => item.id === row.id)
    // if (index > -1) {
    //   dataArray.splice(index, 1)
    //   ElMessage.success('删除成功')
    // }
  })
}
// 获取假期设置列表
const getHolidaySettingsList = () => {
  // tableLoading.value = true;
  listHolidaySettings({...page.value})
  .then(res => {
    // tableLoading.value = false;
    holidayData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
    // tableLoading.value = false;
  })
};
// 获取年假规则列表
const getAnnualLeaveSettingList = () => {
    // 按状态过滤
    if (attendanceStatus.value) {
      filtered = filtered.filter(item => item.status === attendanceStatus.value);
    }
  listAnnualLeaveSettingList({...page.value})
  .then(res => {
    // console.log(res.data)
    annualData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
  })
};
// 获取加班规则列表
const getOvertimeSettingList = () => {
    filteredAttendanceData.value = filtered;
  };
  listOvertimeSettingList({...page.value})
  .then(res => {
    // console.log(res.data)
    overtimeData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
  })
};
// 获取工作时间规则列表
const getWorkingHoursSettingList = () => {
  // 导出打卡记录
  const exportAttendance = () => {
    ElMessage.success("导出功能开发中...");
  };
  listWorkingHoursSettingList({...page.value})
  .then(res => {
    // console.log(res.data)
    worktimeData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
  })
};
onMounted(() => {
  getHolidaySettingsList()
  getAnnualLeaveSettingList()
  getOvertimeSettingList()
  getWorkingHoursSettingList()
  initAttendanceData()
  console.log('考勤管理页面加载完成')
})
  // 初始化打卡记录假数据
  const initAttendanceData = () => {
    const mockData = [
      {
        id: 1,
        employeeName: "陈志强",
        department: "技术部",
        date: "2025-08-15",
        clockInTime: "09:00:00",
        clockOutTime: "18:00:00",
        workHours: "8.0h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 2,
        employeeName: "李雪梅",
        department: "市场部",
        date: "2025-08-16",
        clockInTime: "08:58:00",
        clockOutTime: "18:05:00",
        workHours: "8.12h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 3,
        employeeName: "王建华",
        department: "人事部",
        date: "2025-08-16",
        clockInTime: "09:02:00",
        clockOutTime: "18:00:00",
        workHours: "7.97h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 4,
        employeeName: "赵晓丽",
        department: "财务部",
        date: "2025-09-02",
        clockInTime: "08:55:00",
        clockOutTime: "18:10:00",
        workHours: "8.25h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 5,
        employeeName: "张国庆",
        department: "技术部",
        date: "2025-09-02",
        clockInTime: "09:00:00",
        clockOutTime: "18:30:00",
        workHours: "8.5h",
        status: "normal",
        location: "公司总部",
        remark: "加班",
      },
      {
        id: 6,
        employeeName: "刘明辉",
        department: "运营部",
        date: "2025-09-03",
        clockInTime: "09:05:00",
        clockOutTime: "18:00:00",
        workHours: "7.92h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 7,
        employeeName: "孙丽华",
        department: "设计部",
        date: "2025-09-03",
        clockInTime: "08:59:00",
        clockOutTime: "18:02:00",
        workHours: "8.05h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 8,
        employeeName: "周建军",
        department: "销售部",
        date: "2025-09-04",
        clockInTime: "09:15:00",
        clockOutTime: "18:00:00",
        workHours: "7.75h",
        status: "late",
        location: "公司总部",
        remark: "交通堵塞",
      },
      {
        id: 9,
        employeeName: "吴小芳",
        department: "客服部",
        date: "2025-09-04",
        clockInTime: "09:01:00",
        clockOutTime: "18:00:00",
        workHours: "7.98h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 10,
        employeeName: "马文杰",
        department: "技术部",
        date: "2025-09-05",
        clockInTime: "08:57:00",
        clockOutTime: "17:30:00",
        workHours: "7.55h",
        status: "early",
        location: "公司总部",
        remark: "有急事提前离开",
      },
      {
        id: 11,
        employeeName: "林晓东",
        department: "行政部",
        date: "2025-09-05",
        clockInTime: "09:03:00",
        clockOutTime: "18:08:00",
        workHours: "8.08h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 12,
        employeeName: "黄美玲",
        department: "财务部",
        date: "2025-09-06",
        clockInTime: "",
        clockOutTime: "",
        workHours: "0h",
        status: "absent",
        location: "",
        remark: "请病假",
      },
      {
        id: 13,
        employeeName: "郑海涛",
        department: "市场部",
        date: "2025-08-14",
        clockInTime: "09:00:00",
        clockOutTime: "18:00:00",
        workHours: "8.0h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 14,
        employeeName: "谢丽娟",
        department: "人事部",
        date: "2025-08-20",
        clockInTime: "08:58:00",
        clockOutTime: "18:03:00",
        workHours: "8.08h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 15,
        employeeName: "何志伟",
        department: "技术部",
        date: "2025-08-21",
        clockInTime: "09:10:00",
        clockOutTime: "18:00:00",
        workHours: "7.83h",
        status: "late",
        location: "公司总部",
        remark: "",
      },
      {
        id: 16,
        employeeName: "许雅芳",
        department: "设计部",
        date: "2025-08-22",
        clockInTime: "09:01:00",
        clockOutTime: "18:00:00",
        workHours: "7.98h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 17,
        employeeName: "邓建平",
        department: "运营部",
        date: "2025-09-10",
        clockInTime: "08:59:00",
        clockOutTime: "18:05:00",
        workHours: "8.1h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
      {
        id: 18,
        employeeName: "曾小红",
        department: "客服部",
        date: "2025-09-11",
        clockInTime: "09:02:00",
        clockOutTime: "18:00:00",
        workHours: "7.97h",
        status: "normal",
        location: "公司总部",
        remark: "",
      },
    ];
onUnmounted(() => {
  // 清理工作
  dialogVisible.value = false
  currentType.value = ''
  currentAction.value = ''
  currentEditId.value = ''
})
    attendanceData.value = mockData;
    filteredAttendanceData.value = mockData;
  };
  // 删除项目
  const deleteItem = (type, row) => {
    ElMessageBox.confirm("确定要删除这个项目吗?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      let ids = [];
      let dataArray;
      if (type === "holiday") {
        ids.push(row.id);
        delHolidaySettings(ids)
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("删除成功");
              ids = [];
              getHolidaySettingsList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      } else if (type === "annual") {
        ids.push(row.id);
        delAnnualLeaveSetting(ids)
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("删除成功");
              ids = [];
              getAnnualLeaveSettingList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      } else if (type === "overtime") {
        ids.push(row.id);
        delOvertimeSetting(ids)
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("删除成功");
              ids = [];
              getOvertimeSettingList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      } else if (type === "worktime") {
        ids.push(row.id);
        delWorkingHoursSetting(ids)
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("删除成功");
              ids = [];
              getWorkingHoursSettingList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      }
      // const index = dataArray.findIndex(item => item.id === row.id)
      // if (index > -1) {
      //   dataArray.splice(index, 1)
      //   ElMessage.success('删除成功')
      // }
    });
  };
  // 获取假期设置列表
  const getHolidaySettingsList = () => {
    // tableLoading.value = true;
    listHolidaySettings({ ...page.value })
      .then(res => {
        // tableLoading.value = false;
        holidayData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(err => {
        // tableLoading.value = false;
      });
  };
  // 获取年假规则列表
  const getAnnualLeaveSettingList = () => {
    listAnnualLeaveSettingList({ ...page.value })
      .then(res => {
        // console.log(res.data)
        annualData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(err => {});
  };
  // 获取加班规则列表
  const getOvertimeSettingList = () => {
    listOvertimeSettingList({ ...page.value })
      .then(res => {
        // console.log(res.data)
        overtimeData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(err => {});
  };
  // 获取工作时间规则列表
  const getWorkingHoursSettingList = () => {
    listWorkingHoursSettingList({ ...page.value })
      .then(res => {
        // console.log(res.data)
        worktimeData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(err => {});
  };
  onMounted(() => {
    getHolidaySettingsList();
    getAnnualLeaveSettingList();
    getOvertimeSettingList();
    getWorkingHoursSettingList();
    initAttendanceData();
    console.log("考勤管理页面加载完成");
  });
  onUnmounted(() => {
    // 清理工作
    dialogVisible.value = false;
    currentType.value = "";
    currentAction.value = "";
    currentEditId.value = "";
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
}
  .app-container {
    padding: 20px;
  }
.tab-content {
  padding: 20px 0;
}
  .tab-content {
    padding: 20px 0;
  }
.dialog-footer {
  text-align: right;
}
  .dialog-footer {
    text-align: right;
  }
:deep(.el-tabs__content) {
  padding: 20px;
}
  :deep(.el-tabs__content) {
    padding: 20px;
  }
:deep(.el-form-item) {
  margin-bottom: 20px;
}
  :deep(.el-form-item) {
    margin-bottom: 20px;
  }
</style>
src/views/collaborativeApproval/knowledgeBase/index.vue
@@ -3,144 +3,173 @@
    <div class="search_form">
      <div>
        <span class="search_title">知识标题:</span>
        <el-input
          v-model="searchForm.title"
          style="width: 240px"
          placeholder="请输入知识标题搜索"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
        <el-input v-model="searchForm.title"
                  style="width: 240px"
                  placeholder="请输入知识标题搜索"
                  @change="handleQuery"
                  clearable
                  :prefix-icon="Search" />
        <span class="search_title ml10">知识类型:</span>
        <el-select v-model="searchForm.type" clearable @change="handleQuery" style="width: 240px">
          <el-option label="合同特批" :value="'contract'" />
          <el-option label="审批案例" :value="'approval'" />
          <el-option label="解决方案" :value="'solution'" />
          <el-option label="经验总结" :value="'experience'" />
          <el-option label="操作指南" :value="'guide'" />
        <el-select v-model="searchForm.type"
                   clearable
                   @change="handleQuery"
                   style="width: 240px">
          <el-option label="合同特批"
                     :value="'contract'" />
          <el-option label="审批案例"
                     :value="'approval'" />
          <el-option label="解决方案"
                     :value="'solution'" />
          <el-option label="经验总结"
                     :value="'experience'" />
          <el-option label="操作指南"
                     :value="'guide'" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
        <el-button type="primary"
                   @click="handleQuery"
                   style="margin-left: 10px">
          搜索
        </el-button>
      </div>
      <div>
        <el-button @click="handleExport" style="margin-right: 10px">导出</el-button>
        <el-button type="primary" @click="openForm('add')">新增知识</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <el-button @click="handleExport"
                   style="margin-right: 10px">导出</el-button>
        <el-button type="primary"
                   @click="openForm('add')">新增知识</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
        :total="page.total"
      ></PIMTable>
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :isSelection="true"
                @selection-change="handleSelectionChange"
                :tableLoading="tableLoading"
                @pagination="pagination"
                :total="page.total"></PIMTable>
    </div>
    <!-- 新增/编辑知识弹窗 -->
    <el-dialog
      v-model="dialogVisible"
      :title="dialogTitle"
      width="800px"
      :close-on-click-modal="false"
    >
      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
    <el-dialog v-model="dialogVisible"
               :title="dialogTitle"
               width="800px"
               :close-on-click-modal="false">
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="知识标题" prop="title">
              <el-input v-model="form.title" placeholder="请输入知识标题" />
            <el-form-item label="知识标题"
                          prop="title">
              <el-input v-model="form.title"
                        placeholder="请输入知识标题" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="知识类型" prop="type">
              <el-select v-model="form.type" placeholder="请选择知识类型" style="width: 100%">
                <el-option label="合同特批" value="contract" />
                <el-option label="审批案例" value="approval" />
                <el-option label="解决方案" value="solution" />
                <el-option label="经验总结" value="experience" />
                <el-option label="操作指南" value="guide" />
            <el-form-item label="知识类型"
                          prop="type">
              <el-select v-model="form.type"
                         placeholder="请选择知识类型"
                         style="width: 100%">
                <el-option label="合同特批"
                           value="contract" />
                <el-option label="审批案例"
                           value="approval" />
                <el-option label="解决方案"
                           value="solution" />
                <el-option label="经验总结"
                           value="experience" />
                <el-option label="操作指南"
                           value="guide" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="适用场景" prop="scenario">
              <el-input v-model="form.scenario" placeholder="请输入适用场景" />
            <el-form-item label="适用场景"
                          prop="scenario">
              <el-input v-model="form.scenario"
                        placeholder="请输入适用场景" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="解决效率" prop="efficiency">
              <el-select v-model="form.efficiency" placeholder="请选择解决效率" style="width: 100%">
                <el-option label="显著提升" value="high" />
                <el-option label="一般提升" value="medium" />
                <el-option label="轻微提升" value="low" />
            <el-form-item label="解决效率"
                          prop="efficiency">
              <el-select v-model="form.efficiency"
                         placeholder="请选择解决效率"
                         style="width: 100%">
                <el-option label="显著提升"
                           value="high" />
                <el-option label="一般提升"
                           value="medium" />
                <el-option label="轻微提升"
                           value="low" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="问题描述" prop="problem">
          <el-input
            v-model="form.problem"
            type="textarea"
            :rows="3"
            placeholder="请描述遇到的问题"
          />
        <el-form-item label="问题描述"
                      prop="problem">
          <el-input v-model="form.problem"
                    type="textarea"
                    :rows="3"
                    placeholder="请描述遇到的问题" />
        </el-form-item>
        <el-form-item label="解决方案" prop="solution">
          <el-input
            v-model="form.solution"
            type="textarea"
            :rows="4"
            placeholder="请详细描述解决方案"
          />
        <el-form-item label="解决方案"
                      prop="solution">
          <el-input v-model="form.solution"
                    type="textarea"
                    :rows="4"
                    placeholder="请详细描述解决方案" />
        </el-form-item>
        <el-form-item label="关键要点" prop="keyPoints">
          <el-input
            v-model="form.keyPoints"
            type="textarea"
            :rows="3"
            placeholder="请输入关键要点,用逗号分隔"
          />
        <el-form-item label="关键要点"
                      prop="keyPoints">
          <el-input v-model="form.keyPoints"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入关键要点,用逗号分隔" />
        </el-form-item>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="创建人" prop="creator">
              <el-input v-model="form.creator" placeholder="请输入创建人" />
            <el-form-item label="创建人"
                          prop="creator">
              <el-input v-model="form.creator"
                        placeholder="请输入创建人" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="使用次数" prop="usageCount">
              <el-input-number v-model="form.usageCount" :min="0" style="width: 100%" />
            <el-form-item label="使用次数"
                          prop="usageCount">
              <el-input-number v-model="form.usageCount"
                               :min="0"
                               style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="submitForm">确定</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="submitForm">确定</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 查看知识详情弹窗 -->
    <el-dialog
      v-model="viewDialogVisible"
      title="知识详情"
      width="900px"
      :close-on-click-modal="false"
    >
    <el-dialog v-model="viewDialogVisible"
               title="知识详情"
               width="900px"
               :close-on-click-modal="false">
      <div class="knowledge-detail">
        <el-descriptions :column="2" border>
          <el-descriptions-item label="知识标题" :span="2">
        <el-descriptions :column="2"
                         border>
          <el-descriptions-item label="知识标题"
                                :span="2">
            <span class="detail-title">{{ currentKnowledge.title }}</span>
          </el-descriptions-item>
          <el-descriptions-item label="知识类型">
@@ -166,31 +195,25 @@
            {{ currentKnowledge.createTime }}
          </el-descriptions-item>
        </el-descriptions>
        <div class="detail-section">
          <h4>问题描述</h4>
          <div class="detail-content">{{ currentKnowledge.problem }}</div>
        </div>
        <div class="detail-section">
          <h4>解决方案</h4>
          <div class="detail-content">{{ currentKnowledge.solution }}</div>
        </div>
        <div class="detail-section">
          <h4>关键要点</h4>
          <div class="key-points">
            <el-tag
              v-for="(point, index) in currentKnowledge.keyPoints.split(',')"
              :key="index"
              type="success"
              style="margin-right: 8px; margin-bottom: 8px;"
            >
            <el-tag v-for="(point, index) in currentKnowledge.keyPoints.split(',')"
                    :key="index"
                    type="success"
                    style="margin-right: 8px; margin-bottom: 8px;">
              {{ point.trim() }}
            </el-tag>
          </div>
        </div>
        <div class="detail-section">
          <h4>使用统计</h4>
          <div class="usage-stats">
@@ -217,11 +240,11 @@
          </div>
        </div>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="copyKnowledge">复制知识</el-button>
          <el-button @click="viewDialogVisible = false">关闭</el-button>
          <el-button type="primary" @click="copyKnowledge">复制知识</el-button>
          <!-- <el-button type="success" @click="markAsFavorite">收藏@</el-button> -->
        </span>
      </template>
@@ -230,333 +253,42 @@
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import { listKnowledgeBase, delKnowledgeBase,addKnowledgeBase,updateKnowledgeBase } from "@/api/collaborativeApproval/knowledgeBase.js";
  import { Search } from "@element-plus/icons-vue";
  import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  import {
    listKnowledgeBase,
    delKnowledgeBase,
    addKnowledgeBase,
    updateKnowledgeBase,
  } from "@/api/collaborativeApproval/knowledgeBase.js";
// 表单验证规则
const rules = {
  title: [
    { required: true, message: "请输入知识标题", trigger: "blur" }
  ],
  type: [
    { required: true, message: "请选择知识类型", trigger: "change" }
  ],
  problem: [
    { required: true, message: "请描述遇到的问题", trigger: "blur" }
  ],
  solution: [
    { required: true, message: "请详细描述解决方案", trigger: "blur" }
  ]
};
// 响应式数据
const data = reactive({
  searchForm: {
    title: "",
    type: "",
  },
  tableLoading: false,
  page: {
    current: 1,
    size: 20,
    total: 0,
  },
  tableData: [],
  selectedIds: [],
  form: {
    title: "",
    type: "",
    scenario: "",
    efficiency: "",
    problem: "",
    solution: "",
    keyPoints: "",
    creator: "",
    usageCount: 0
  },
  dialogVisible: false,
  dialogTitle: "",
  dialogType: "add",
  viewDialogVisible: false,
  currentKnowledge: {}
});
const {
  searchForm,
  tableLoading,
  page,
  tableData,
  selectedIds,
  form,
  dialogVisible,
  dialogTitle,
  dialogType,
  viewDialogVisible,
  currentKnowledge
} = toRefs(data);
// 表单引用
const formRef = ref();
// 表格列配置
const tableColumn = ref([
  {
    label: "知识标题",
    prop: "title",
    showOverflowTooltip: true,
  },
  {
    label: "知识类型",
    prop: "type",
    dataType: "tag",
    formatData: (params) => {
      const typeMap = {
        contract: "合同特批",
        approval: "审批案例",
        solution: "解决方案",
        experience: "经验总结",
        guide: "操作指南"
      };
      return typeMap[params] || params;
    },
    formatType: (params) => {
      const typeMap = {
        contract: "success",
        approval: "warning",
        solution: "primary",
        experience: "info",
        guide: "danger"
      };
      return typeMap[params] || "info";
    }
  },
  {
    label: "适用场景",
    prop: "scenario",
    width: 150,
    showOverflowTooltip: true,
  },
  {
    label: "解决效率",
    prop: "efficiency",
    dataType: "tag",
    formatData: (params) => {
      const efficiencyMap = {
        high: "显著提升",
        medium: "一般提升",
        low: "轻微提升"
      };
      return efficiencyMap[params] || params;
    },
    formatType: (params) => {
      const typeMap = {
        high: "success",
        medium: "warning",
        low: "info"
      };
      return typeMap[params] || "info";
    }
  },
  {
    label: "使用次数",
    prop: "usageCount",
    width: 100,
    align: "center"
  },
  {
    label: "创建人",
    prop: "creator",
    width: 120,
  },
  {
    label: "创建时间",
    prop: "createTime",
    width: 180,
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 200,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        }
      },
      {
        name: "查看",
        type: "text",
        clickFun: (row) => {
          viewKnowledge(row);
        }
      }
    ]
  }
]);
// 模拟数据
// let mockData = [
//   {
//     id: "1",
//     title: "特殊合同审批流程优化方案",
//     type: "contract",
//     scenario: "大额合同快速审批",
//     efficiency: "high",
//     problem: "大额合同审批流程复杂,审批时间长,影响业务进展",
//     solution: "建立绿色通道,对符合条件的合同采用简化审批流程,由部门负责人直接审批,平均审批时间从3天缩短至1天",
//     keyPoints: "绿色通道条件,简化流程,审批权限,时间控制",
//     creator: "张经理",
//     usageCount: 15,
//     createTime: "2024-01-15 10:30:00"
//   },
//   {
//     id: "2",
//     title: "跨部门协作审批经验总结",
//     type: "experience",
//     scenario: "多部门协作项目",
//     efficiency: "medium",
//     problem: "跨部门项目审批时,各部门意见不统一,审批进度缓慢",
//     solution: "建立项目协调机制,指定项目负责人,定期召开协调会议,统一各方意见后再进行审批",
//     keyPoints: "项目协调,定期会议,统一意见,负责人制度",
//     creator: "李主管",
//     usageCount: 8,
//     createTime: "2024-01-14 15:20:00"
//   },
//   {
//     id: "3",
//     title: "紧急采购审批操作指南",
//     type: "guide",
//     scenario: "紧急采购需求",
//     efficiency: "high",
//     problem: "紧急采购时审批流程复杂,无法满足紧急需求",
//     solution: "制定紧急采购审批标准,明确紧急程度分级,不同级别采用不同审批流程,确保紧急需求得到及时处理",
//     keyPoints: "紧急分级,标准制定,流程简化,及时处理",
//     creator: "王专员",
//     usageCount: 12,
//     createTime: "2024-01-13 09:15:00"
//   }
// ];
// 知识标题模板
const titleTemplates = [
  "{type}审批流程优化方案",
  "{scenario}处理经验总结",
  "{type}特殊情况处理指南",
  "{scenario}快速审批方案",
  "{type}标准化操作流程",
  "{scenario}问题解决方案",
  "{type}最佳实践总结",
  "{scenario}效率提升方案"
];
// 知识类型配置
const knowledgeTypes = [
  { type: "contract", label: "合同特批", efficiency: "high" },
  { type: "approval", label: "审批案例", efficiency: "medium" },
  { type: "solution", label: "解决方案", efficiency: "high" },
  { type: "experience", label: "经验总结", efficiency: "medium" },
  { type: "guide", label: "操作指南", efficiency: "low" }
];
// 场景列表
const scenarios = ["大额合同审批", "跨部门协作", "紧急采购", "特殊申请", "流程优化", "问题处理", "标准化建设", "效率提升"];
// 自动生成新数据
const generateNewData = () => {
  const newId = (mockData.length + 1).toString();
  const now = new Date();
  const randomType = knowledgeTypes[Math.floor(Math.random() * knowledgeTypes.length)];
  const randomScenario = scenarios[Math.floor(Math.random() * scenarios.length)];
  // 生成随机标题
  let title = titleTemplates[Math.floor(Math.random() * titleTemplates.length)];
  title = title
    .replace('{type}', randomType.label)
    .replace('{scenario}', randomScenario);
  const newKnowledge = {
    id: newId,
    title: title,
    type: randomType.type,
    scenario: randomScenario,
    efficiency: randomType.efficiency,
    problem: `在${randomScenario}过程中遇到的问题描述...`,
    solution: `针对${randomScenario}的解决方案和操作步骤...`,
    keyPoints: "关键要点1,关键要点2,关键要点3,关键要点4",
    creator: ["张经理", "李主管", "王专员", "刘总监"][Math.floor(Math.random() * 4)],
    usageCount: Math.floor(Math.random() * 20) + 1,
    createTime: now.toLocaleString()
  // 表单验证规则
  const rules = {
    title: [{ required: true, message: "请输入知识标题", trigger: "blur" }],
    type: [{ required: true, message: "请选择知识类型", trigger: "change" }],
    problem: [{ required: true, message: "请描述遇到的问题", trigger: "blur" }],
    solution: [
      { required: true, message: "请详细描述解决方案", trigger: "blur" },
    ],
  };
  // 添加到数据开头
  mockData.unshift(newKnowledge);
  // 保持数据量在合理范围内(最多保留30条)
  if (mockData.length > 30) {
    mockData = mockData.slice(0, 30);
  }
  console.log(`[${new Date().toLocaleString()}] 自动生成新知识: ${title}`);
};
// 生命周期
onMounted(() => {
  getList();
  startAutoRefresh();
});
// 开始自动刷新
const startAutoRefresh = () => {
  setInterval(() => {
    generateNewData();
    getList();
  }, 600000); // 10分钟刷新一次 (10 * 60 * 1000 = 600000ms)
};
// 查询数据
const handleQuery = () => {
  page.value.current = 1;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  listKnowledgeBase({...page.value, ...searchForm.value})
  .then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
};
// 分页处理
const pagination = (obj) => {
  page.value.current = obj.page;
  page.value.size = obj.limit;
  handleQuery();
};
// 选择变化处理
const handleSelectionChange = (selection) => {
  selectedIds.value = selection.map(item => item.id);
};
// 打开表单
const openForm = (type, row = null) => {
  dialogType.value = type;
  if (type === "add") {
    dialogTitle.value = "新增知识";
    // 重置表单
    Object.assign(form.value, {
  // 响应式数据
  const data = reactive({
    searchForm: {
      title: "",
      type: "",
    },
    tableLoading: false,
    page: {
      current: 1,
      size: 20,
      total: 0,
    },
    tableData: [],
    selectedIds: [],
    form: {
      title: "",
      type: "",
      scenario: "",
@@ -565,262 +297,581 @@
      solution: "",
      keyPoints: "",
      creator: "",
      usageCount: 0
    });
  } else if (type === "edit" && row) {
    dialogTitle.value = "编辑知识";
    Object.assign(form.value, {
      id: row.id,
      title: row.title,
      type: row.type,
      scenario: row.scenario,
      efficiency: row.efficiency,
      problem: row.problem,
      solution: row.solution,
      keyPoints: row.keyPoints,
      creator: row.creator,
      usageCount: row.usageCount
    });
  }
  dialogVisible.value = true;
};
// 查看知识详情
const viewKnowledge = (row) => {
  currentKnowledge.value = { ...row };
  viewDialogVisible.value = true;
};
// 获取类型标签类型
const getTypeTagType = (type) => {
  const typeMap = {
    contract: "success",
    approval: "warning",
    solution: "primary",
    experience: "info",
    guide: "danger"
  };
  return typeMap[type] || "info";
};
// 获取类型标签文本
const getTypeLabel = (type) => {
  const typeMap = {
    contract: "合同特批",
    approval: "审批案例",
    solution: "解决方案",
    experience: "经验总结",
    guide: "操作指南"
  };
  return typeMap[type] || type;
};
// 获取效率标签类型
const getEfficiencyTagType = (efficiency) => {
  const typeMap = {
    high: "success",
    medium: "warning",
    low: "info"
  };
  return typeMap[efficiency] || "info";
};
// 获取效率标签文本
const getEfficiencyLabel = (efficiency) => {
  const efficiencyMap = {
    high: "显著提升",
    medium: "一般提升",
    low: "轻微提升"
  };
  return efficiencyMap[efficiency] || efficiency;
};
// 获取效率提升百分比
const getEfficiencyScore = (efficiency) => {
  const scoreMap = {
    high: 40,
    medium: 25,
    low: 15
  };
  return scoreMap[efficiency] || 0;
};
// 获取平均节省时间
const getTimeSaved = (efficiency) => {
  const timeMap = {
    high: "2-3天",
    medium: "1-2天",
    low: "0.5-1天"
  };
  return timeMap[efficiency] || "未知";
};
// 复制知识
const copyKnowledge = () => {
  const knowledgeText = `
    知识标题:${currentKnowledge.value.title}
    知识类型:${getTypeLabel(currentKnowledge.value.type)}
    适用场景:${currentKnowledge.value.scenario}
    问题描述:${currentKnowledge.value.problem}
    解决方案:${currentKnowledge.value.solution}
    关键要点:${currentKnowledge.value.keyPoints}
    创建人:${currentKnowledge.value.creator}
  `.trim();
  // 复制到剪贴板
  navigator.clipboard.writeText(knowledgeText).then(() => {
    ElMessage.success("知识内容已复制到剪贴板");
  }).catch(() => {
    ElMessage.error("复制失败,请手动复制");
      usageCount: 0,
    },
    dialogVisible: false,
    dialogTitle: "",
    dialogType: "add",
    viewDialogVisible: false,
    currentKnowledge: {},
  });
};
// 收藏知识
const markAsFavorite = () => {
  // 增加使用次数
  const index = mockData.findIndex(item => item.id === currentKnowledge.value.id);
  if (index !== -1) {
    mockData[index].usageCount += 1;
    currentKnowledge.value.usageCount += 1;
  }
  const {
    searchForm,
    tableLoading,
    page,
    tableData,
    selectedIds,
    form,
    dialogVisible,
    dialogTitle,
    dialogType,
    viewDialogVisible,
    currentKnowledge,
  } = toRefs(data);
  ElMessage.success("已收藏,使用次数+1");
};
  // 表单引用
  const formRef = ref();
// 提交知识表单
const submitForm = async () => {
  try {
    await formRef.value.validate();
    if (dialogType.value === "add") {
      // 新增知识
      addKnowledgeBase({...form.value}).then(res => {
        if(res.code == 200){
          ElMessage.success("添加成功");
          dialogVisible.value = false;
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
    } else {
      updateKnowledgeBase({...form.value}).then(res => {
        if(res.code == 200){
          ElMessage.success("更新成功");
          dialogVisible.value = false;
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
  // 表格列配置
  const tableColumn = ref([
    {
      label: "知识标题",
      prop: "title",
      showOverflowTooltip: true,
    },
    {
      label: "知识类型",
      prop: "type",
      dataType: "tag",
      formatData: params => {
        const typeMap = {
          contract: "合同特批",
          approval: "审批案例",
          solution: "解决方案",
          experience: "经验总结",
          guide: "操作指南",
        };
        return typeMap[params] || params;
      },
      formatType: params => {
        const typeMap = {
          contract: "success",
          approval: "warning",
          solution: "primary",
          experience: "info",
          guide: "danger",
        };
        return typeMap[params] || "info";
      },
    },
    {
      label: "适用场景",
      prop: "scenario",
      width: 150,
      showOverflowTooltip: true,
    },
    {
      label: "解决效率",
      prop: "efficiency",
      dataType: "tag",
      formatData: params => {
        const efficiencyMap = {
          high: "显著提升",
          medium: "一般提升",
          low: "轻微提升",
        };
        return efficiencyMap[params] || params;
      },
      formatType: params => {
        const typeMap = {
          high: "success",
          medium: "warning",
          low: "info",
        };
        return typeMap[params] || "info";
      },
    },
    {
      label: "使用次数",
      prop: "usageCount",
      width: 100,
      align: "center",
    },
    {
      label: "创建人",
      prop: "creator",
      width: 120,
    },
    {
      label: "创建时间",
      prop: "createTime",
      width: 180,
    },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: 200,
      operation: [
        {
          name: "编辑",
          type: "text",
          clickFun: row => {
            openForm("edit", row);
          },
        },
        {
          name: "查看",
          type: "text",
          clickFun: row => {
            viewKnowledge(row);
          },
        },
      ],
    },
  ]);
  // 模拟数据
  // let mockData = [
  //   {
  //     id: "1",
  //     title: "特殊合同审批流程优化方案",
  //     type: "contract",
  //     scenario: "大额合同快速审批",
  //     efficiency: "high",
  //     problem: "大额合同审批流程复杂,审批时间长,影响业务进展",
  //     solution: "建立绿色通道,对符合条件的合同采用简化审批流程,由部门负责人直接审批,平均审批时间从3天缩短至1天",
  //     keyPoints: "绿色通道条件,简化流程,审批权限,时间控制",
  //     creator: "张经理",
  //     usageCount: 15,
  //     createTime: "2024-01-15 10:30:00"
  //   },
  //   {
  //     id: "2",
  //     title: "跨部门协作审批经验总结",
  //     type: "experience",
  //     scenario: "多部门协作项目",
  //     efficiency: "medium",
  //     problem: "跨部门项目审批时,各部门意见不统一,审批进度缓慢",
  //     solution: "建立项目协调机制,指定项目负责人,定期召开协调会议,统一各方意见后再进行审批",
  //     keyPoints: "项目协调,定期会议,统一意见,负责人制度",
  //     creator: "李主管",
  //     usageCount: 8,
  //     createTime: "2024-01-14 15:20:00"
  //   },
  //   {
  //     id: "3",
  //     title: "紧急采购审批操作指南",
  //     type: "guide",
  //     scenario: "紧急采购需求",
  //     efficiency: "high",
  //     problem: "紧急采购时审批流程复杂,无法满足紧急需求",
  //     solution: "制定紧急采购审批标准,明确紧急程度分级,不同级别采用不同审批流程,确保紧急需求得到及时处理",
  //     keyPoints: "紧急分级,标准制定,流程简化,及时处理",
  //     creator: "王专员",
  //     usageCount: 12,
  //     createTime: "2024-01-13 09:15:00"
  //   }
  // ];
  // 知识标题模板
  const titleTemplates = [
    "{type}审批流程优化方案",
    "{scenario}处理经验总结",
    "{type}特殊情况处理指南",
    "{scenario}快速审批方案",
    "{type}标准化操作流程",
    "{scenario}问题解决方案",
    "{type}最佳实践总结",
    "{scenario}效率提升方案",
  ];
  // 知识类型配置
  const knowledgeTypes = [
    { type: "contract", label: "合同特批", efficiency: "high" },
    { type: "approval", label: "审批案例", efficiency: "medium" },
    { type: "solution", label: "解决方案", efficiency: "high" },
    { type: "experience", label: "经验总结", efficiency: "medium" },
    { type: "guide", label: "操作指南", efficiency: "low" },
  ];
  // 场景列表
  const scenarios = [
    "大额合同审批",
    "跨部门协作",
    "紧急采购",
    "特殊申请",
    "流程优化",
    "问题处理",
    "标准化建设",
    "效率提升",
  ];
  // 自动生成新数据
  const generateNewData = () => {
    const newId = (mockData.length + 1).toString();
    const now = new Date();
    const randomType =
      knowledgeTypes[Math.floor(Math.random() * knowledgeTypes.length)];
    const randomScenario =
      scenarios[Math.floor(Math.random() * scenarios.length)];
    // 生成随机标题
    let title = titleTemplates[Math.floor(Math.random() * titleTemplates.length)];
    title = title
      .replace("{type}", randomType.label)
      .replace("{scenario}", randomScenario);
    const newKnowledge = {
      id: newId,
      title: title,
      type: randomType.type,
      scenario: randomScenario,
      efficiency: randomType.efficiency,
      problem: `在${randomScenario}过程中遇到的问题描述...`,
      solution: `针对${randomScenario}的解决方案和操作步骤...`,
      keyPoints: "关键要点1,关键要点2,关键要点3,关键要点4",
      creator: ["张经理", "李主管", "王专员", "刘总监"][
        Math.floor(Math.random() * 4)
      ],
      usageCount: Math.floor(Math.random() * 20) + 1,
      createTime: now.toLocaleString(),
    };
    // 添加到数据开头
    mockData.unshift(newKnowledge);
    // 保持数据量在合理范围内(最多保留30条)
    if (mockData.length > 30) {
      mockData = mockData.slice(0, 30);
    }
  } catch (error) {
    console.error("表单验证失败:", error);
  }
};
// 删除知识
const handleDelete = () => {
  if (selectedIds.value.length === 0) {
    ElMessage.warning("请选择要删除的知识");
    return;
  }
    console.log(`[${new Date().toLocaleString()}] 自动生成新知识: ${title}`);
  };
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    // console.log(selectedIds.value);
    delKnowledgeBase(selectedIds.value).then(res => {
      if(res.code == 200){
        ElMessage.success("删除成功");
        selectedIds.value = [];
        getList();
      }
    })
  }).catch(() => {
    // 用户取消
  // 生命周期
  onMounted(() => {
    getList();
    startAutoRefresh();
  });
};
// 导出
const { proxy } = getCurrentInstance()
const handleExport = () => {
  proxy.download('/knowledgeBase/export', { ...searchForm.value }, '知识库.xlsx')
}
  // 开始自动刷新
  const startAutoRefresh = () => {
    setInterval(() => {
      generateNewData();
      getList();
    }, 600000); // 10分钟刷新一次 (10 * 60 * 1000 = 600000ms)
  };
  // 查询数据
  const handleQuery = () => {
    page.value.current = 1;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    listKnowledgeBase({ ...page.value, ...searchForm.value })
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(err => {
        tableLoading.value = false;
      });
  };
  // 分页处理
  const pagination = obj => {
    page.value.current = obj.page;
    page.value.size = obj.limit;
    handleQuery();
  };
  // 选择变化处理
  const handleSelectionChange = selection => {
    selectedIds.value = selection.map(item => item.id);
  };
  // 打开表单
  const openForm = (type, row = null) => {
    dialogType.value = type;
    if (type === "add") {
      dialogTitle.value = "新增知识";
      // 重置表单
      Object.assign(form.value, {
        title: "",
        type: "",
        scenario: "",
        efficiency: "",
        problem: "",
        solution: "",
        keyPoints: "",
        creator: "",
        usageCount: 0,
      });
    } else if (type === "edit" && row) {
      dialogTitle.value = "编辑知识";
      Object.assign(form.value, {
        id: row.id,
        title: row.title,
        type: row.type,
        scenario: row.scenario,
        efficiency: row.efficiency,
        problem: row.problem,
        solution: row.solution,
        keyPoints: row.keyPoints,
        creator: row.creator,
        usageCount: row.usageCount,
      });
    }
    dialogVisible.value = true;
  };
  // 查看知识详情
  const viewKnowledge = row => {
    currentKnowledge.value = { ...row };
    viewDialogVisible.value = true;
  };
  // 获取类型标签类型
  const getTypeTagType = type => {
    const typeMap = {
      contract: "success",
      approval: "warning",
      solution: "primary",
      experience: "info",
      guide: "danger",
    };
    return typeMap[type] || "info";
  };
  // 获取类型标签文本
  const getTypeLabel = type => {
    const typeMap = {
      contract: "合同特批",
      approval: "审批案例",
      solution: "解决方案",
      experience: "经验总结",
      guide: "操作指南",
    };
    return typeMap[type] || type;
  };
  // 获取效率标签类型
  const getEfficiencyTagType = efficiency => {
    const typeMap = {
      high: "success",
      medium: "warning",
      low: "info",
    };
    return typeMap[efficiency] || "info";
  };
  // 获取效率标签文本
  const getEfficiencyLabel = efficiency => {
    const efficiencyMap = {
      high: "显著提升",
      medium: "一般提升",
      low: "轻微提升",
    };
    return efficiencyMap[efficiency] || efficiency;
  };
  // 获取效率提升百分比
  const getEfficiencyScore = efficiency => {
    const scoreMap = {
      high: 40,
      medium: 25,
      low: 15,
    };
    return scoreMap[efficiency] || 0;
  };
  // 获取平均节省时间
  const getTimeSaved = efficiency => {
    const timeMap = {
      high: "2-3天",
      medium: "1-2天",
      low: "0.5-1天",
    };
    return timeMap[efficiency] || "未知";
  };
  // 复制知识
  const copyKnowledge = () => {
    const knowledgeText = `
        知识标题:${currentKnowledge.value.title}
        知识类型:${getTypeLabel(currentKnowledge.value.type)}
        适用场景:${currentKnowledge.value.scenario}
        问题描述:${currentKnowledge.value.problem}
        解决方案:${currentKnowledge.value.solution}
        关键要点:${currentKnowledge.value.keyPoints}
        创建人:${currentKnowledge.value.creator}
      `.trim();
    // 复制到剪贴板
    navigator.clipboard
      .writeText(knowledgeText)
      .then(() => {
        ElMessage.success("知识内容已复制到剪贴板");
      })
      .catch(() => {
        ElMessage.error("复制失败,请手动复制");
      });
  };
  // 收藏知识
  const markAsFavorite = () => {
    // 增加使用次数
    const index = mockData.findIndex(
      item => item.id === currentKnowledge.value.id
    );
    if (index !== -1) {
      mockData[index].usageCount += 1;
      currentKnowledge.value.usageCount += 1;
    }
    ElMessage.success("已收藏,使用次数+1");
  };
  // 提交知识表单
  const submitForm = async () => {
    try {
      await formRef.value.validate();
      if (dialogType.value === "add") {
        // 新增知识
        addKnowledgeBase({ ...form.value })
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("添加成功");
              dialogVisible.value = false;
              getList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      } else {
        updateKnowledgeBase({ ...form.value })
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("更新成功");
              dialogVisible.value = false;
              getList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      }
    } catch (error) {
      console.error("表单验证失败:", error);
    }
  };
  // 删除知识
  const handleDelete = () => {
    if (selectedIds.value.length === 0) {
      ElMessage.warning("请选择要删除的知识");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        // console.log(selectedIds.value);
        delKnowledgeBase(selectedIds.value).then(res => {
          if (res.code == 200) {
            ElMessage.success("删除成功");
            selectedIds.value = [];
            getList();
          }
        });
      })
      .catch(() => {
        // 用户取消
      });
  };
  // 导出
  const { proxy } = getCurrentInstance();
  const handleExport = () => {
    proxy.download(
      "/knowledgeBase/export",
      { ...searchForm.value },
      "知识库.xlsx"
    );
  };
</script>
<style scoped>
.auto-refresh-info {
  margin-bottom: 15px;
}
  .auto-refresh-info {
    margin-bottom: 15px;
  }
.auto-refresh-info .el-alert {
  border-radius: 8px;
}
  .auto-refresh-info .el-alert {
    border-radius: 8px;
  }
.dialog-footer {
  text-align: right;
}
  .dialog-footer {
    text-align: right;
  }
.knowledge-detail {
  padding: 20px 0;
}
  .knowledge-detail {
    padding: 20px 0;
  }
.detail-title {
  font-size: 18px;
  font-weight: bold;
  color: #303133;
}
  .detail-title {
    font-size: 18px;
    font-weight: bold;
    color: #303133;
  }
.detail-section {
  margin-top: 24px;
}
  .detail-section {
    margin-top: 24px;
  }
.detail-section h4 {
  margin: 0 0 12px 0;
  font-size: 16px;
  font-weight: 600;
  color: #303133;
  border-left: 4px solid #409eff;
  padding-left: 12px;
}
  .detail-section h4 {
    margin: 0 0 12px 0;
    font-size: 16px;
    font-weight: 600;
    color: #303133;
    border-left: 4px solid #409eff;
    padding-left: 12px;
  }
.detail-content {
  background: #f8f9fa;
  padding: 16px;
  border-radius: 6px;
  line-height: 1.6;
  color: #606266;
  white-space: pre-wrap;
}
  .detail-content {
    background: #f8f9fa;
    padding: 16px;
    border-radius: 6px;
    line-height: 1.6;
    color: #606266;
    white-space: pre-wrap;
  }
.key-points {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
  .key-points {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }
.usage-stats {
  margin-top: 16px;
}
  .usage-stats {
    margin-top: 16px;
  }
.stat-item {
  text-align: center;
  padding: 20px;
  background: #f8f9fa;
  border-radius: 8px;
}
  .stat-item {
    text-align: center;
    padding: 20px;
    background: #f8f9fa;
    border-radius: 8px;
  }
.stat-number {
  font-size: 24px;
  font-weight: bold;
  color: #409eff;
  margin-bottom: 8px;
}
  .stat-number {
    font-size: 24px;
    font-weight: bold;
    color: #409eff;
    margin-bottom: 8px;
  }
.stat-label {
  font-size: 14px;
  color: #909399;
}
  .stat-label {
    font-size: 14px;
    color: #909399;
  }
</style>
src/views/collaborativeApproval/notificationManagement/index.vue
@@ -3,300 +3,314 @@
    <div class="search_form">
      <div>
        <span class="search_title">通知标题:</span>
        <el-input
          v-model="searchForm.title"
          style="width: 240px"
          placeholder="请输入通知标题搜索"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
        <el-input v-model="searchForm.title"
                  style="width: 240px"
                  placeholder="请输入通知标题搜索"
                  @change="handleQuery"
                  clearable
                  :prefix-icon="Search" />
        <span class="search_title ml10">通知类型:</span>
        <el-select v-model="searchForm.type" clearable @change="handleQuery" style="width: 240px">
          <el-option label="放假通知" :value="'holiday'" />
          <el-option label="处罚通知" :value="'penalty'" />
          <el-option label="开会通知" :value="'meeting'" />
          <el-option label="临时通知" :value="'temporary'" />
          <el-option label="正式通知" :value="'formal'" />
        <el-select v-model="searchForm.type"
                   clearable
                   @change="handleQuery"
                   style="width: 240px">
          <el-option label="放假通知"
                     :value="'holiday'" />
          <el-option label="处罚通知"
                     :value="'penalty'" />
          <el-option label="开会通知"
                     :value="'meeting'" />
          <el-option label="临时通知"
                     :value="'temporary'" />
          <el-option label="正式通知"
                     :value="'formal'" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
        <el-button type="primary"
                   @click="handleQuery"
                   style="margin-left: 10px">
          搜索
        </el-button>
      </div>
      <div>
        <el-button type="primary" @click="openForm('add')">新增通知</el-button>
        <el-button type="success" @click="openMeetingDialog">在线会议</el-button>
        <el-button type="warning" @click="openFileShareDialog">文件共享</el-button>
        <el-button type="primary"
                   @click="openForm('add')">新增通知</el-button>
        <el-button type="success"
                   @click="openMeetingDialog">在线会议</el-button>
        <el-button type="warning"
                   @click="openFileShareDialog">文件共享</el-button>
        <!-- <el-button type="info" @click="refreshEmployees">刷新员工</el-button> -->
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
        :total="page.total"
      ></PIMTable>
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :isSelection="true"
                @selection-change="handleSelectionChange"
                :tableLoading="tableLoading"
                @pagination="pagination"
                :total="page.total"></PIMTable>
    </div>
    <!-- 新增/编辑通知弹窗 -->
    <el-dialog
      v-model="dialogVisible"
      :title="dialogTitle"
      width="800px"
      :close-on-click-modal="false"
    >
      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
    <el-dialog v-model="dialogVisible"
               :title="dialogTitle"
               width="800px"
               :close-on-click-modal="false">
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="通知标题" prop="title">
              <el-input v-model="form.title" placeholder="请输入通知标题" />
            <el-form-item label="通知标题"
                          prop="title">
              <el-input v-model="form.title"
                        placeholder="请输入通知标题" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="通知类型" prop="type">
              <el-select v-model="form.type" placeholder="请选择通知类型" style="width: 100%">
                <el-option label="放假通知" value="holiday" />
                <el-option label="处罚通知" value="penalty" />
                <el-option label="开会通知" value="meeting" />
                <el-option label="临时通知" value="temporary" />
                <el-option label="正式通知" value="formal" />
            <el-form-item label="通知类型"
                          prop="type">
              <el-select v-model="form.type"
                         placeholder="请选择通知类型"
                         style="width: 100%">
                <el-option label="放假通知"
                           value="holiday" />
                <el-option label="处罚通知"
                           value="penalty" />
                <el-option label="开会通知"
                           value="meeting" />
                <el-option label="临时通知"
                           value="temporary" />
                <el-option label="正式通知"
                           value="formal" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="优先级" prop="priority">
              <el-select v-model="form.priority" placeholder="请选择优先级" style="width: 100%">
                <el-option label="普通" value="low" />
                <el-option label="重要" value="medium" />
                <el-option label="紧急" value="high" />
            <el-form-item label="优先级"
                          prop="priority">
              <el-select v-model="form.priority"
                         placeholder="请选择优先级"
                         style="width: 100%">
                <el-option label="普通"
                           value="low" />
                <el-option label="重要"
                           value="medium" />
                <el-option label="紧急"
                           value="high" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="有效期至" prop="expireDate">
              <el-date-picker
                v-model="form.expireDate"
                type="date"
                placeholder="请选择有效期"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                style="width: 100%"
              />
            <el-form-item label="有效期至"
                          prop="expireDate">
              <el-date-picker v-model="form.expireDate"
                              type="date"
                              placeholder="请选择有效期"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="接收部门" prop="departments">
          <el-select
            v-model="form.departments"
            multiple
            placeholder="请选择接收部门"
            style="width: 100%"
          >
            <el-option
              v-for="dept in departments"
              :key="dept"
              :label="dept"
              :value="dept"
            />
        <el-form-item label="接收部门"
                      prop="departments">
          <el-select v-model="form.departments"
                     multiple
                     placeholder="请选择接收部门"
                     style="width: 100%">
            <el-option v-for="dept in departments"
                       :key="dept"
                       :label="dept"
                       :value="dept" />
          </el-select>
        </el-form-item>
        <el-form-item label="同步方式" prop="syncMethods">
        <el-form-item label="同步方式"
                      prop="syncMethods">
          <el-checkbox-group v-model="form.syncMethods">
            <el-checkbox
              v-for="method in syncMethods"
              :key="method.value"
              :label="method.value"
            >
            <el-checkbox v-for="method in syncMethods"
                         :key="method.value"
                         :label="method.value">
              {{ method.label }}
            </el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="通知内容" prop="content">
          <el-input
            v-model="form.content"
            type="textarea"
            :rows="4"
            placeholder="请输入通知内容"
          />
        <el-form-item label="通知内容"
                      prop="content">
          <el-input v-model="form.content"
                    type="textarea"
                    :rows="4"
                    placeholder="请输入通知内容" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="submitForm">确定</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="submitForm">确定</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 在线会议弹窗 -->
    <el-dialog
      v-model="meetingDialogVisible"
      title="创建在线会议"
      width="700px"
      :close-on-click-modal="false"
    >
      <el-form ref="meetingFormRef" :model="meetingForm" :rules="meetingRules" label-width="120px">
        <el-form-item label="会议标题" prop="title">
          <el-input v-model="meetingForm.title" placeholder="请输入会议标题" />
    <el-dialog v-model="meetingDialogVisible"
               title="创建在线会议"
               width="700px"
               :close-on-click-modal="false">
      <el-form ref="meetingFormRef"
               :model="meetingForm"
               :rules="meetingRules"
               label-width="120px">
        <el-form-item label="会议标题"
                      prop="title">
          <el-input v-model="meetingForm.title"
                    placeholder="请输入会议标题" />
        </el-form-item>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="开始时间" prop="startTime">
              <el-date-picker
                v-model="meetingForm.startTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择开始时间"
                style="width: 100%"
              />
            <el-form-item label="开始时间"
                          prop="startTime">
              <el-date-picker v-model="meetingForm.startTime"
                              type="datetime"
                              value-format="YYYY-MM-DD HH:mm:ss"
                              format="YYYY-MM-DD HH:mm:ss"
                              placeholder="请选择开始时间"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="会议时长" prop="duration">
              <el-input-number
                v-model="meetingForm.duration"
                :min="15"
                :max="480"
                :step="15"
                style="width: 100%"
              />
            <el-form-item label="会议时长"
                          prop="duration">
              <el-input-number v-model="meetingForm.duration"
                               :min="15"
                               :max="480"
                               :step="15"
                               style="width: 100%" />
              <span style="margin-left: 10px">分钟</span>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="会议平台" prop="platform">
          <el-select v-model="meetingForm.platform" placeholder="请选择会议平台" style="width: 100%">
            <el-option
              v-for="platform in meetingPlatforms"
              :key="platform.value"
              :label="platform.label"
              :value="platform.value"
            />
        <el-form-item label="会议平台"
                      prop="platform">
          <el-select v-model="meetingForm.platform"
                     placeholder="请选择会议平台"
                     style="width: 100%">
            <el-option v-for="platform in meetingPlatforms"
                       :key="platform.value"
                       :label="platform.label"
                       :value="platform.value" />
          </el-select>
        </el-form-item>
        <el-form-item label="参会人员" prop="participants">
          <el-select
            v-model="meetingForm.participants"
            multiple
            filterable
            remote
            :remote-method="filterEmployees"
            :loading="employeesLoading"
            placeholder="请选择参会人员"
            style="width: 100%"
          >
            <el-option-group
              v-for="group in employeeGroups"
              :key="group.label"
              :label="group.label"
            >
                             <el-option
                 v-for="employee in group.options"
                 :key="employee.value"
                 :label="`${employee.label} (${employee.dept})`"
                 :value="employee.value"
               >
                 <div style="display: flex; justify-content: space-between; align-items: center;">
                   <div>
                     <div style="font-weight: 500;">{{ employee.label }}</div>
                     <div style="color: #909399; font-size: 12px;">{{ employee.dept }}</div>
                   </div>
                   <div style="text-align: right; font-size: 12px; color: #909399;">
                     <div v-if="employee.phone">{{ employee.phone }}</div>
                     <div v-if="employee.email">{{ employee.email }}</div>
                   </div>
                 </div>
               </el-option>
        <el-form-item label="参会人员"
                      prop="participants">
          <el-select v-model="meetingForm.participants"
                     multiple
                     filterable
                     remote
                     :remote-method="filterEmployees"
                     :loading="employeesLoading"
                     placeholder="请选择参会人员"
                     style="width: 100%">
            <el-option-group v-for="group in employeeGroups"
                             :key="group.label"
                             :label="group.label">
              <el-option v-for="employee in group.options"
                         :key="employee.value"
                         :label="`${employee.label} (${employee.dept})`"
                         :value="employee.value">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                  <div>
                    <div style="font-weight: 500;">{{ employee.label }}</div>
                    <div style="color: #909399; font-size: 12px;">{{ employee.dept }}</div>
                  </div>
                  <div style="text-align: right; font-size: 12px; color: #909399;">
                    <div v-if="employee.phone">{{ employee.phone }}</div>
                    <div v-if="employee.email">{{ employee.email }}</div>
                  </div>
                </div>
              </el-option>
            </el-option-group>
          </el-select>
          <div style="margin-top: 8px; color: #909399; font-size: 12px;">
            已选择 {{ meetingForm.participants.length }} 人
          </div>
          <!-- 已选择人员详情 -->
          <div v-if="meetingForm.participants.length > 0" style="margin-top: 10px;">
            <el-tag
              v-for="participantId in meetingForm.participants"
              :key="participantId"
              closable
              @close="removeParticipant(participantId)"
              style="margin-right: 8px; margin-bottom: 8px;"
            >
          <div v-if="meetingForm.participants.length > 0"
               style="margin-top: 10px;">
            <el-tag v-for="participantId in meetingForm.participants"
                    :key="participantId"
                    closable
                    @close="removeParticipant(participantId)"
                    style="margin-right: 8px; margin-bottom: 8px;">
              {{ getEmployeeName(participantId) }}
            </el-tag>
          </div>
        </el-form-item>
        <el-form-item label="会议描述" prop="description">
          <el-input
            v-model="meetingForm.description"
            type="textarea"
            :rows="3"
            placeholder="请输入会议描述"
          />
        <el-form-item label="会议描述"
                      prop="description">
          <el-input v-model="meetingForm.description"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入会议描述" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="createMeeting">创建会议</el-button>
          <el-button @click="meetingDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="createMeeting">创建会议</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 文件共享弹窗 -->
    <el-dialog
      v-model="fileShareDialogVisible"
      title="文件共享"
      width="700px"
      :close-on-click-modal="false"
    >
      <el-form ref="fileShareFormRef" :model="fileShareForm" :rules="fileShareRules" label-width="120px">
        <el-form-item label="共享标题" prop="title">
          <el-input v-model="fileShareForm.title" placeholder="请输入共享标题" />
    <el-dialog v-model="fileShareDialogVisible"
               title="文件共享"
               width="700px"
               :close-on-click-modal="false">
      <el-form ref="fileShareFormRef"
               :model="fileShareForm"
               :rules="fileShareRules"
               label-width="120px">
        <el-form-item label="共享标题"
                      prop="title">
          <el-input v-model="fileShareForm.title"
                    placeholder="请输入共享标题" />
        </el-form-item>
        <el-form-item label="共享描述" prop="description">
          <el-input
            v-model="fileShareForm.description"
            type="textarea"
            :rows="3"
            placeholder="请输入共享描述"
          />
        <el-form-item label="共享描述"
                      prop="description">
          <el-input v-model="fileShareForm.description"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入共享描述" />
        </el-form-item>
        <el-form-item label="接收部门" prop="departments">
          <el-select
            v-model="fileShareForm.departments"
            multiple
            placeholder="请选择接收部门"
            style="width: 100%"
          >
            <el-option
              v-for="dept in departments"
              :key="dept"
              :label="dept"
              :value="dept"
            />
        <el-form-item label="接收部门"
                      prop="departments">
          <el-select v-model="fileShareForm.departments"
                     multiple
                     placeholder="请选择接收部门"
                     style="width: 100%">
            <el-option v-for="dept in departments"
                       :key="dept"
                       :label="dept"
                       :value="dept" />
          </el-select>
        </el-form-item>
        <el-form-item label="上传文件" prop="files">
          <el-upload
            ref="uploadRef"
            :auto-upload="false"
            :on-change="handleFileChange"
            :on-remove="removeFile"
            :file-list="fileList"
            multiple
            :limit="10"
            accept=".doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.txt,.jpg,.jpeg,.png,.gif"
          >
        <el-form-item label="上传文件"
                      prop="files">
          <el-upload ref="uploadRef"
                     :auto-upload="false"
                     :on-change="handleFileChange"
                     :on-remove="removeFile"
                     :file-list="fileList"
                     multiple
                     :limit="10"
                     accept=".doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.txt,.jpg,.jpeg,.png,.gif">
            <el-button type="primary">选择文件</el-button>
            <template #tip>
              <div class="el-upload__tip">
@@ -308,8 +322,9 @@
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="shareFiles">共享文件</el-button>
          <el-button @click="fileShareDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="shareFiles">共享文件</el-button>
        </span>
      </template>
    </el-dialog>
@@ -317,539 +332,872 @@
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { onMounted, ref, reactive, toRefs, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import { userListNoPageByTenantId } from "@/api/system/user.js";
import { staffOnJobListPage } from "@/api/personnelManagement/employeeRecord.js";
import { listNotification, addNotification, updateNotification, delNotification,addOnlineMeeting,addFileSharing } from "@/api/collaborativeApproval/notificationManagement.js";
import { id } from "element-plus/es/locales.mjs";
  import { Search } from "@element-plus/icons-vue";
  import { onMounted, ref, reactive, toRefs, computed } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  import { userListNoPageByTenantId } from "@/api/system/user.js";
  import { staffOnJobListPage } from "@/api/personnelManagement/employeeRecord.js";
  import {
    listNotification,
    addNotification,
    updateNotification,
    delNotification,
    addOnlineMeeting,
    addFileSharing,
  } from "@/api/collaborativeApproval/notificationManagement.js";
  import { id } from "element-plus/es/locales.mjs";
// 表单验证规则
const rules = {
  title: [
    { required: true, message: "请输入通知标题", trigger: "blur" }
  ],
  type: [
    { required: true, message: "请选择通知类型", trigger: "change" }
  ],
  content: [
    { required: true, message: "请输入通知内容", trigger: "blur" }
  ]
};
const meetingRules = {
  title: [
    { required: true, message: "请输入会议标题", trigger: "blur" }
  ],
  startTime: [
    { required: true, message: "请选择会议开始时间", trigger: "change" }
  ],
  participants: [
    { required: true, message: "请选择参会人员", trigger: "change" }
  ]
};
const fileShareRules = {
  title: [
    { required: true, message: "请输入共享标题", trigger: "blur" }
  ],
  description: [
    { required: true, message: "请输入共享描述", trigger: "blur" }
  ]
};
// 响应式数据
const data = reactive({
  searchForm: {
    title: "",
    type: "",
    status: "",
  },
  tableLoading: false,
  page: {
    current: 1,
    size: 20,
    total: 0,
  },
  tableData: [],
  selectedIds: [],
  // 新增通知相关
  form: {
    title: "",
    type: "",
    priority: "",
    content: "",
    departments: [],
    expireDate: "",
    syncMethods: []
  },
  dialogVisible: false,
  dialogTitle: "",
  dialogType: "add",
  // 在线会议相关
  meetingDialogVisible: false,
  meetingForm: {
    title: "",
    startTime: "",
    duration: 60,
    participants: [],
    description: "",
    platform: "wechat"
  },
  // 文件共享相关
  fileShareDialogVisible: false,
  fileShareForm: {
    title: "",
    description: "",
    departments: [],
    files: []
  },
  fileList: []
});
const {
  searchForm,
  tableLoading,
  page,
  tableData,
  selectedIds,
  form,
  dialogVisible,
  dialogTitle,
  dialogType,
  meetingDialogVisible,
  meetingForm,
  fileShareDialogVisible,
  fileShareForm,
  fileList
} = toRefs(data);
// 表单引用
const formRef = ref();
const meetingFormRef = ref();
const fileShareFormRef = ref();
// 表格列配置
const tableColumn = ref([
  {
    label: "通知标题",
    prop: "title",
    showOverflowTooltip: true,
  },
  {
    label: "通知类型",
    prop: "type",
    dataType: "tag",
    formatData: (params) => {
      const typeMap = {
        holiday: "放假通知",
        penalty: "处罚通知",
        meeting: "开会通知",
        temporary: "临时通知",
        formal: "正式通知"
      };
      return typeMap[params] || params;
    },
    formatType: (params) => {
      const typeMap = {
        holiday: "success",
        penalty: "danger",
        meeting: "warning",
        temporary: "info",
        formal: "primary"
      };
      return typeMap[params] || "info";
    }
  },
  {
    label: "优先级",
    prop: "priority",
    dataType: "tag",
    formatData: (params) => {
      const priorityMap = {
        low: "普通",
        medium: "重要",
        high: "紧急"
      };
      return priorityMap[params] || params;
    },
    formatType: (params) => {
      const typeMap = {
        low: "info",
        medium: "warning",
        high: "danger"
      };
      return typeMap[params] || "info";
    }
  },
  {
    label: "状态",
    prop: "status",
    dataType: "tag",
    formatData: (params) => {
      const statusMap = {
        draft: "草稿",
        published: "已发布",
        expired: "已过期"
      };
      return statusMap[params] || params;
    },
    formatType: (params) => {
      const typeMap = {
        draft: "info",
        published: "success",
        expired: "danger"
      };
      return typeMap[params] || "info";
    }
  },
  {
    label: "接收部门",
    prop: "departments",
    width: 150,
    showOverflowTooltip: true,
    formatData: (params) => {
      if (!params || params.length === 0) return "全部部门";
      return params.join(", ");
    }
  },
  {
    label: "有效期至",
    prop: "expireDate",
    width: 150,
    formatData: (params) => {
      if (!params) return "永久有效";
      return params;
    }
  },
  {
    label: "创建时间",
    prop: "createTime",
    width: 180,
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 280,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        }
      },
      {
        name: "发布",
        type: "text",
        clickFun: (row) => {
          publishNotification(row);
        },
        // disabled: (row) => row.status === "published"
      },
      {
        name: "撤回",
        type: "text",
        clickFun: (row) => {
          revokeNotification(row);
        },
        // disabled: (row) => row.status !== "published"
      }
    ]
  }
]);
// 通知标题模板
const titleTemplates = [
  "关于{year}年{holiday}放假安排的通知",
  "{dept}部门{meeting}会议通知",
  "员工{behavior}行为规范提醒",
  "{company}重要事项通知",
  "{dept}部门工作安排通知",
  "关于{project}项目进度的通知",
  "{dept}部门人员调整通知",
  "公司{policy}政策更新通知"
];
// 通知类型配置
const notificationTypes = [
  { type: "holiday", label: "放假通知", priority: "high" },
  { type: "meeting", label: "开会通知", priority: "medium" },
  { type: "penalty", label: "处罚通知", priority: "high" },
  { type: "temporary", label: "临时通知", priority: "low" },
  { type: "formal", label: "正式通知", priority: "medium" }
];
// 部门列表
const departments = ["技术部", "销售部", "人事部", "财务部", "运营部", "市场部", "客服部"];
// 人员列表
const employees = ref([]);
const employeesLoading = ref(false);
// 获取在职员工列表
const getEmployeesList = async () => {
  try {
    employeesLoading.value = true;
    // 优先使用系统用户接口(按租户获取)
    const userResponse = await userListNoPageByTenantId();
    if (userResponse.data) {
      employees.value = userResponse.data.map(user => ({
        label: user.nickName || user.userName || '未知姓名',
        value: user.userId || user.id,
        dept: user.dept?.deptName || '未知部门',
        phone: user.phonenumber || '',
        email: user.email || '',
        status: user.status || '0'
      })).filter(user => user.status === '0'); // 只显示正常状态的用户
    } else {
      // 如果系统用户接口失败,使用员工台账接口
      const response = await staffOnJobListPage({
        pageNum: 1,
        pageSize: 1000,
        staffState: 1 // 在职状态
      });
      if (response.data && response.data.records) {
        employees.value = response.data.records.map(employee => ({
          label: employee.staffName || employee.name || '未知姓名',
          value: employee.staffNo || employee.id || employee.staffId,
          dept: employee.deptName || employee.department || '未知部门',
          phone: employee.phone || employee.mobile || '',
          email: employee.email || '',
          status: '0'
        }));
      }
    }
  } catch (error) {
    console.error('获取员工列表失败:', error);
    // 如果接口都失败,使用默认数据
    employees.value = [
      { label: "张三", value: "001", dept: "技术部", phone: "13800138001", email: "zhangsan@company.com", status: "0" },
      { label: "李四", value: "002", dept: "销售部", phone: "13800138002", email: "lisi@company.com", status: "0" },
      { label: "王五", value: "003", dept: "人事部", phone: "13800138003", email: "wangwu@company.com", status: "0" }
    ];
  } finally {
    employeesLoading.value = false;
  }
};
// 员工分组
const employeeGroups = computed(() => {
  const groups = {};
  employees.value.forEach(employee => {
    const dept = employee.dept || '其他部门';
    if (!groups[dept]) {
      groups[dept] = [];
    }
    groups[dept].push(employee);
  });
  // 按部门名称排序,确保显示顺序一致
  return Object.keys(groups)
    .sort()
    .map(dept => ({
      label: dept,
      options: groups[dept].sort((a, b) => a.label.localeCompare(b.label, 'zh-CN'))
    }));
});
// 过滤员工(远程搜索)
const filterEmployees = (query) => {
  if (query !== '') {
    const lowerQuery = query.toLowerCase();
    return employees.value.filter(employee =>
      employee.label.toLowerCase().includes(lowerQuery) ||
      employee.dept.toLowerCase().includes(lowerQuery) ||
      (employee.phone && employee.phone.includes(query)) ||
      (employee.email && employee.email.toLowerCase().includes(lowerQuery))
    );
  } else {
    return employees.value;
  }
};
// 刷新员工列表
const refreshEmployees = async () => {
  ElMessage.info("正在刷新员工列表...");
  await getEmployeesList();
  // 统计各部门人数
  const deptStats = {};
  employees.value.forEach(emp => {
    const dept = emp.dept || '其他部门';
    deptStats[dept] = (deptStats[dept] || 0) + 1;
  });
  const deptInfo = Object.entries(deptStats)
    .map(([dept, count]) => `${dept}: ${count}人`)
    .join(', ');
  ElMessage.success(`员工列表刷新完成,共 ${employees.value.length} 人 (${deptInfo})`);
};
// 获取员工姓名
const getEmployeeName = (employeeId) => {
  const employee = employees.value.find(emp => emp.value === employeeId);
  return employee ? employee.label : '未知人员';
};
// 获取员工详细信息
const getEmployeeInfo = (employeeId) => {
  const employee = employees.value.find(emp => emp.value === employeeId);
  if (!employee) return null;
  return {
    name: employee.label,
    dept: employee.dept,
    phone: employee.phone,
    email: employee.email
  };
};
// 移除参会人员
const removeParticipant = (participantId) => {
  const index = meetingForm.value.participants.indexOf(participantId);
  if (index > -1) {
    meetingForm.value.participants.splice(index, 1);
  }
};
// 同步方式选项
const syncMethods = [
  { label: "企业微信", value: "wechat" },
  { label: "钉钉", value: "dingtalk" },
  { label: "邮件", value: "email" },
  { label: "短信", value: "sms" }
];
// 会议平台选项
const meetingPlatforms = [
  { label: "企业微信会议", value: "wechat" },
  { label: "钉钉会议", value: "dingtalk" },
  { label: "腾讯会议", value: "tencent" },
  { label: "Zoom", value: "zoom" }
];
// 自动生成新数据
const generateNewData = () => {
  const newId = (mockData.length + 1).toString();
  const now = new Date();
  const randomType = notificationTypes[Math.floor(Math.random() * notificationTypes.length)];
  const randomDept = departments[Math.floor(Math.random() * departments.length)];
  // 生成随机标题
  let title = titleTemplates[Math.floor(Math.random() * titleTemplates.length)];
  title = title
    .replace('{year}', now.getFullYear())
    .replace('{holiday}', ['春节', '国庆', '中秋', '元旦'][Math.floor(Math.random() * 4)])
    .replace('{dept}', randomDept)
    .replace('{meeting}', ['周例会', '月度总结', '项目评审', '培训会议'][Math.floor(Math.random() * 4)])
    .replace('{behavior}', ['考勤', '着装', '工作态度', '团队协作'][Math.floor(Math.random() * 4)])
    .replace('{company}', ['公司', '集团', '总部'][Math.floor(Math.random() * 4)])
    .replace('{project}', ['数字化转型', '产品升级', '市场拓展', '人才培养'][Math.floor(Math.random() * 4)])
    .replace('{policy}', ['考勤', '薪酬', '福利', '晋升'][Math.floor(Math.random() * 4)]);
  // 随机状态
  const statuses = ['draft', 'published'];
  const randomStatus = statuses[Math.floor(Math.random() * statuses.length)];
  // 随机优先级
  const priorities = ['low', 'medium', 'high'];
  const randomPriority = priorities[Math.floor(Math.random() * priorities.length)];
  const newNotification = {
    id: newId,
    title: title,
    type: randomType.type,
    priority: randomPriority,
    status: randomStatus,
    content: `这是${title}的详细内容,请相关人员注意查看...`,
    departments: [randomDept],
    expireDate: new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], // 30天后过期
    syncMethods: ["wechat", "dingtalk"],
    createTime: now.toLocaleString()
  // 表单验证规则
  const rules = {
    title: [{ required: true, message: "请输入通知标题", trigger: "blur" }],
    type: [{ required: true, message: "请选择通知类型", trigger: "change" }],
    content: [{ required: true, message: "请输入通知内容", trigger: "blur" }],
  };
  // 添加到数据开头
  mockData.unshift(newNotification);
  const meetingRules = {
    title: [{ required: true, message: "请输入会议标题", trigger: "blur" }],
    startTime: [
      { required: true, message: "请选择会议开始时间", trigger: "change" },
    ],
    participants: [
      { required: true, message: "请选择参会人员", trigger: "change" },
    ],
  };
  // 保持数据量在合理范围内(最多保留20条)
  if (mockData.length > 20) {
    mockData = mockData.slice(0, 20);
  }
  const fileShareRules = {
    title: [{ required: true, message: "请输入共享标题", trigger: "blur" }],
    description: [{ required: true, message: "请输入共享描述", trigger: "blur" }],
  };
  console.log(`[${new Date().toLocaleString()}] 自动生成新通知: ${title}`);
};
// 生命周期
onMounted(() => {
  getList();
  getEmployeesList(); // 获取员工列表
  startAutoRefresh();
});
// 开始自动刷新
const startAutoRefresh = () => {
  setInterval(() => {
    generateNewData();
    getList();
  }, 600000); // 10分钟刷新一次 (10 * 60 * 1000 = 600000ms)
};
// 查询数据
const handleQuery = () => {
  page.value.current = 1;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  listNotification({...page.value, ...searchForm.value})
  .then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.value.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
};
// 分页处理
const pagination = (obj) => {
  page.value.current = obj.page;
  page.value.size = obj.limit;
  handleQuery();
};
// 选择变化处理
const handleSelectionChange = (selection) => {
  selectedIds.value = selection.map(item => item.id);
};
// 打开表单
const openForm = (type, row = null) => {
  dialogType.value = type;
  if (type === "add") {
    dialogTitle.value = "新增通知";
    // 重置表单
    Object.assign(form.value, {
      id: "",
  // 响应式数据
  const data = reactive({
    searchForm: {
      title: "",
      type: "",
      status: "",
    },
    tableLoading: false,
    page: {
      current: 1,
      size: 20,
      total: 0,
    },
    tableData: [],
    selectedIds: [],
    // 新增通知相关
    form: {
      title: "",
      type: "",
      priority: "",
      content: "",
      departments: [],
      expireDate: "",
      status: "draft",
      syncMethods: []
      syncMethods: [],
    },
    dialogVisible: false,
    dialogTitle: "",
    dialogType: "add",
    // 在线会议相关
    meetingDialogVisible: false,
    meetingForm: {
      title: "",
      startTime: "",
      duration: 60,
      participants: [],
      description: "",
      platform: "wechat",
    },
    // 文件共享相关
    fileShareDialogVisible: false,
    fileShareForm: {
      title: "",
      description: "",
      departments: [],
      files: [],
    },
    fileList: [],
  });
  const {
    searchForm,
    tableLoading,
    page,
    tableData,
    selectedIds,
    form,
    dialogVisible,
    dialogTitle,
    dialogType,
    meetingDialogVisible,
    meetingForm,
    fileShareDialogVisible,
    fileShareForm,
    fileList,
  } = toRefs(data);
  // 表单引用
  const formRef = ref();
  const meetingFormRef = ref();
  const fileShareFormRef = ref();
  // 表格列配置
  const tableColumn = ref([
    {
      label: "通知标题",
      prop: "title",
      showOverflowTooltip: true,
    },
    {
      label: "通知类型",
      prop: "type",
      dataType: "tag",
      formatData: params => {
        const typeMap = {
          holiday: "放假通知",
          penalty: "处罚通知",
          meeting: "开会通知",
          temporary: "临时通知",
          formal: "正式通知",
        };
        return typeMap[params] || params;
      },
      formatType: params => {
        const typeMap = {
          holiday: "success",
          penalty: "danger",
          meeting: "warning",
          temporary: "info",
          formal: "primary",
        };
        return typeMap[params] || "info";
      },
    },
    {
      label: "优先级",
      prop: "priority",
      dataType: "tag",
      formatData: params => {
        const priorityMap = {
          low: "普通",
          medium: "重要",
          high: "紧急",
        };
        return priorityMap[params] || params;
      },
      formatType: params => {
        const typeMap = {
          low: "info",
          medium: "warning",
          high: "danger",
        };
        return typeMap[params] || "info";
      },
    },
    {
      label: "状态",
      prop: "status",
      dataType: "tag",
      formatData: params => {
        const statusMap = {
          draft: "草稿",
          published: "已发布",
          expired: "已过期",
        };
        return statusMap[params] || params;
      },
      formatType: params => {
        const typeMap = {
          draft: "info",
          published: "success",
          expired: "danger",
        };
        return typeMap[params] || "info";
      },
    },
    {
      label: "接收部门",
      prop: "departments",
      width: 150,
      showOverflowTooltip: true,
      formatData: params => {
        if (!params || params.length === 0) return "全部部门";
        return params.join(", ");
      },
    },
    {
      label: "有效期至",
      prop: "expireDate",
      width: 150,
      formatData: params => {
        if (!params) return "永久有效";
        return params;
      },
    },
    {
      label: "创建时间",
      prop: "createTime",
      width: 180,
    },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: 280,
      operation: [
        {
          name: "编辑",
          type: "text",
          clickFun: row => {
            openForm("edit", row);
          },
        },
        {
          name: "发布",
          type: "text",
          clickFun: row => {
            publishNotification(row);
          },
          // disabled: (row) => row.status === "published"
        },
        {
          name: "撤回",
          type: "text",
          clickFun: row => {
            revokeNotification(row);
          },
          // disabled: (row) => row.status !== "published"
        },
      ],
    },
  ]);
  // 通知标题模板
  const titleTemplates = [
    "关于{year}年{holiday}放假安排的通知",
    "{dept}部门{meeting}会议通知",
    "员工{behavior}行为规范提醒",
    "{company}重要事项通知",
    "{dept}部门工作安排通知",
    "关于{project}项目进度的通知",
    "{dept}部门人员调整通知",
    "公司{policy}政策更新通知",
  ];
  // 通知类型配置
  const notificationTypes = [
    { type: "holiday", label: "放假通知", priority: "high" },
    { type: "meeting", label: "开会通知", priority: "medium" },
    { type: "penalty", label: "处罚通知", priority: "high" },
    { type: "temporary", label: "临时通知", priority: "low" },
    { type: "formal", label: "正式通知", priority: "medium" },
  ];
  // 部门列表
  const departments = [
    "技术部",
    "销售部",
    "人事部",
    "财务部",
    "运营部",
    "市场部",
    "客服部",
  ];
  // 人员列表
  const employees = ref([]);
  const employeesLoading = ref(false);
  // 获取在职员工列表
  const getEmployeesList = async () => {
    try {
      employeesLoading.value = true;
      // 优先使用系统用户接口(按租户获取)
      const userResponse = await userListNoPageByTenantId();
      if (userResponse.data) {
        employees.value = userResponse.data
          .map(user => ({
            label: user.nickName || user.userName || "未知姓名",
            value: user.userId || user.id,
            dept: user.dept?.deptName || "未知部门",
            phone: user.phonenumber || "",
            email: user.email || "",
            status: user.status || "0",
          }))
          .filter(user => user.status === "0"); // 只显示正常状态的用户
      } else {
        // 如果系统用户接口失败,使用员工台账接口
        const response = await staffOnJobListPage({
          pageNum: 1,
          pageSize: 1000,
          staffState: 1, // 在职状态
        });
        if (response.data && response.data.records) {
          employees.value = response.data.records.map(employee => ({
            label: employee.staffName || employee.name || "未知姓名",
            value: employee.staffNo || employee.id || employee.staffId,
            dept: employee.deptName || employee.department || "未知部门",
            phone: employee.phone || employee.mobile || "",
            email: employee.email || "",
            status: "0",
          }));
        }
      }
    } catch (error) {
      console.error("获取员工列表失败:", error);
      // 如果接口都失败,使用默认数据
      employees.value = [
        {
          label: "张三",
          value: "001",
          dept: "技术部",
          phone: "13800138001",
          email: "zhangsan@company.com",
          status: "0",
        },
        {
          label: "李四",
          value: "002",
          dept: "销售部",
          phone: "13800138002",
          email: "lisi@company.com",
          status: "0",
        },
        {
          label: "王五",
          value: "003",
          dept: "人事部",
          phone: "13800138003",
          email: "wangwu@company.com",
          status: "0",
        },
      ];
    } finally {
      employeesLoading.value = false;
    }
  };
  // 员工分组
  const employeeGroups = computed(() => {
    const groups = {};
    employees.value.forEach(employee => {
      const dept = employee.dept || "其他部门";
      if (!groups[dept]) {
        groups[dept] = [];
      }
      groups[dept].push(employee);
    });
  } else if (type === "edit" && row) {
    dialogTitle.value = "编辑通知";
    // 按部门名称排序,确保显示顺序一致
    return Object.keys(groups)
      .sort()
      .map(dept => ({
        label: dept,
        options: groups[dept].sort((a, b) =>
          a.label.localeCompare(b.label, "zh-CN")
        ),
      }));
  });
  // 过滤员工(远程搜索)
  const filterEmployees = query => {
    if (query !== "") {
      const lowerQuery = query.toLowerCase();
      return employees.value.filter(
        employee =>
          employee.label.toLowerCase().includes(lowerQuery) ||
          employee.dept.toLowerCase().includes(lowerQuery) ||
          (employee.phone && employee.phone.includes(query)) ||
          (employee.email && employee.email.toLowerCase().includes(lowerQuery))
      );
    } else {
      return employees.value;
    }
  };
  // 刷新员工列表
  const refreshEmployees = async () => {
    ElMessage.info("正在刷新员工列表...");
    await getEmployeesList();
    // 统计各部门人数
    const deptStats = {};
    employees.value.forEach(emp => {
      const dept = emp.dept || "其他部门";
      deptStats[dept] = (deptStats[dept] || 0) + 1;
    });
    const deptInfo = Object.entries(deptStats)
      .map(([dept, count]) => `${dept}: ${count}人`)
      .join(", ");
    ElMessage.success(
      `员工列表刷新完成,共 ${employees.value.length} 人 (${deptInfo})`
    );
  };
  // 获取员工姓名
  const getEmployeeName = employeeId => {
    const employee = employees.value.find(emp => emp.value === employeeId);
    return employee ? employee.label : "未知人员";
  };
  // 获取员工详细信息
  const getEmployeeInfo = employeeId => {
    const employee = employees.value.find(emp => emp.value === employeeId);
    if (!employee) return null;
    return {
      name: employee.label,
      dept: employee.dept,
      phone: employee.phone,
      email: employee.email,
    };
  };
  // 移除参会人员
  const removeParticipant = participantId => {
    const index = meetingForm.value.participants.indexOf(participantId);
    if (index > -1) {
      meetingForm.value.participants.splice(index, 1);
    }
  };
  // 同步方式选项
  const syncMethods = [
    { label: "企业微信", value: "wechat" },
    { label: "钉钉", value: "dingtalk" },
    { label: "邮件", value: "email" },
    { label: "短信", value: "sms" },
  ];
  // 会议平台选项
  const meetingPlatforms = [
    { label: "企业微信会议", value: "wechat" },
    { label: "钉钉会议", value: "dingtalk" },
    { label: "腾讯会议", value: "tencent" },
    { label: "Zoom", value: "zoom" },
  ];
  // 自动生成新数据
  const generateNewData = () => {
    const newId = (mockData.length + 1).toString();
    const now = new Date();
    const randomType =
      notificationTypes[Math.floor(Math.random() * notificationTypes.length)];
    const randomDept =
      departments[Math.floor(Math.random() * departments.length)];
    // 生成随机标题
    let title = titleTemplates[Math.floor(Math.random() * titleTemplates.length)];
    title = title
      .replace("{year}", now.getFullYear())
      .replace(
        "{holiday}",
        ["春节", "国庆", "中秋", "元旦"][Math.floor(Math.random() * 4)]
      )
      .replace("{dept}", randomDept)
      .replace(
        "{meeting}",
        ["周例会", "月度总结", "项目评审", "培训会议"][
          Math.floor(Math.random() * 4)
        ]
      )
      .replace(
        "{behavior}",
        ["考勤", "着装", "工作态度", "团队协作"][Math.floor(Math.random() * 4)]
      )
      .replace(
        "{company}",
        ["公司", "集团", "总部"][Math.floor(Math.random() * 4)]
      )
      .replace(
        "{project}",
        ["数字化转型", "产品升级", "市场拓展", "人才培养"][
          Math.floor(Math.random() * 4)
        ]
      )
      .replace(
        "{policy}",
        ["考勤", "薪酬", "福利", "晋升"][Math.floor(Math.random() * 4)]
      );
    // 随机状态
    const statuses = ["draft", "published"];
    const randomStatus = statuses[Math.floor(Math.random() * statuses.length)];
    // 随机优先级
    const priorities = ["low", "medium", "high"];
    const randomPriority =
      priorities[Math.floor(Math.random() * priorities.length)];
    const newNotification = {
      id: newId,
      title: title,
      type: randomType.type,
      priority: randomPriority,
      status: randomStatus,
      content: `这是${title}的详细内容,请相关人员注意查看...`,
      departments: [randomDept],
      expireDate: new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000)
        .toISOString()
        .split("T")[0], // 30天后过期
      syncMethods: ["wechat", "dingtalk"],
      createTime: now.toLocaleString(),
    };
    // 添加到数据开头
    mockData.unshift(newNotification);
    // 保持数据量在合理范围内(最多保留20条)
    if (mockData.length > 20) {
      mockData = mockData.slice(0, 20);
    }
    console.log(`[${new Date().toLocaleString()}] 自动生成新通知: ${title}`);
  };
  // 生命周期
  onMounted(() => {
    getList();
    getEmployeesList(); // 获取员工列表
    startAutoRefresh();
  });
  // 开始自动刷新
  const startAutoRefresh = () => {
    setInterval(() => {
      generateNewData();
      getList();
    }, 600000); // 10分钟刷新一次 (10 * 60 * 1000 = 600000ms)
  };
  // 查询数据
  const handleQuery = () => {
    page.value.current = 1;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    listNotification({ ...page.value, ...searchForm.value })
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.value.total = res.data.total;
      })
      .catch(err => {
        tableLoading.value = false;
      });
  };
  // 分页处理
  const pagination = obj => {
    page.value.current = obj.page;
    page.value.size = obj.limit;
    handleQuery();
  };
  // 选择变化处理
  const handleSelectionChange = selection => {
    selectedIds.value = selection.map(item => item.id);
  };
  // 打开表单
  const openForm = (type, row = null) => {
    dialogType.value = type;
    if (type === "add") {
      dialogTitle.value = "新增通知";
      // 重置表单
      Object.assign(form.value, {
        id: "",
        title: "",
        type: "",
        priority: "",
        content: "",
        departments: [],
        expireDate: "",
        status: "draft",
        syncMethods: [],
      });
    } else if (type === "edit" && row) {
      dialogTitle.value = "编辑通知";
      Object.assign(form.value, {
        id: row.id,
        title: row.title,
        type: row.type,
        priority: row.priority,
        content: row.content || "",
        departments: row.departments || [],
        expireDate: row.expireDate || "",
        status: row.status,
        syncMethods: row.syncMethods || [],
      });
    }
    dialogVisible.value = true;
  };
  // 打开在线会议弹窗
  const openMeetingDialog = () => {
    // 重置表单
    Object.assign(meetingForm.value, {
      title: "",
      startTime: "",
      duration: 60,
      participants: [],
      description: "",
      platform: "wechat",
    });
    meetingDialogVisible.value = true;
  };
  // 打开文件共享弹窗
  const openFileShareDialog = () => {
    // 重置表单
    Object.assign(fileShareForm.value, {
      title: "",
      description: "",
      departments: [],
      files: [],
    });
    fileList.value = [];
    fileShareDialogVisible.value = true;
  };
  // 手动刷新数据
  const manualRefresh = () => {
    generateNewData();
    getList();
    ElMessage.success("手动刷新完成,已生成新通知");
  };
  // 提交通知表单
  const submitForm = async () => {
    try {
      await formRef.value.validate();
      if (dialogType.value === "add") {
        // 新增通知
        addNotification({ ...form.value })
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("添加成功");
              dialogVisible.value = false;
              getList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      } else {
        // 编辑通知
        updateNotification({ ...form.value })
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("更新成功");
              dialogVisible.value = false;
              getList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      }
    } catch (error) {
      console.error("表单验证失败:", error);
    }
  };
  // 创建会议
  const createMeeting = async () => {
    try {
      await meetingFormRef.value.validate();
      // 模拟创建会议
      const meetingInfo = {
        title: meetingForm.value.title,
        startTime: meetingForm.value.startTime,
        duration: meetingForm.value.duration,
        participants: meetingForm.value.participants,
        description: meetingForm.value.description,
        platform: meetingForm.value.platform,
      };
      // 新增会议
      addOnlineMeeting({ ...meetingInfo })
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("会议添加成功");
            meetingDialogVisible.value = false;
            getList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
      // 模拟发送到企业微信/钉钉
      // const platformName = meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "未知平台";
      // ElMessage.success(`会议创建成功!会议ID: ${meetingInfo.meetingId},将通过${platformName}发送通知`);
      // 获取参会人员信息
      const participantNames = meetingForm.value.participants
        .map(participantId => {
          const employee = employees.value.find(
            emp => emp.value === participantId
          );
          return employee ? employee.label : "未知人员";
        })
        .join("、");
      // 获取参会人员详细信息
      const participantDetails = meetingForm.value.participants
        .map(participantId => {
          const employee = employees.value.find(
            emp => emp.value === participantId
          );
          return employee
            ? {
                name: employee.label,
                dept: employee.dept,
                phone: employee.phone,
                email: employee.email,
              }
            : null;
        })
        .filter(Boolean);
      // 将会议信息添加到通知列表
      const meetingNotification = {
        title: `[会议通知] ${meetingInfo.title}`,
        type: "meeting",
        priority: "high",
        status: "published",
        content: `会议时间: ${meetingInfo.startTime},时长: ${
          meetingInfo.duration
        }分钟,平台: ${
          meetingPlatforms.find(p => p.value === meetingForm.value.platform)
            ?.label || "未知平台"
        },参会人员: ${participantNames},共${participantDetails.length}人`,
        departments: [],
        expireDate: "",
        syncMethods: [meetingForm.value.platform],
      };
      addNotification({ ...meetingNotification })
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("添加成功");
            // dialogVisible.value = false;
            getList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
      // mockData.unshift(meetingNotification);
      // getList();
    } catch (error) {
      console.error("会议表单验证失败:", error);
    }
  };
  // 文件上传处理
  const handleFileChange = file => {
    const isLt10M = file.size / 1024 / 1024 < 10;
    if (!isLt10M) {
      ElMessage.error("上传文件大小不能超过 10MB!");
      return false;
    }
    const fileInfo = {
      name: file.name,
      size: file.size,
      type: file.type,
      uid: file.uid,
    };
    fileList.value.push(fileInfo);
    fileShareForm.value.files.push(fileInfo.name);
    return false; // 阻止自动上传
  };
  // 移除文件
  const removeFile = file => {
    const index = fileList.value.findIndex(item => item.uid === file.uid);
    if (index !== -1) {
      const index2 = fileShareForm.value.files.findIndex(
        item => item.uid === file.uid
      );
      if (index2 !== -1) {
        fileShareForm.value.files.splice(index2, 1);
      }
      fileList.value.splice(index, 1);
    }
  };
  // 共享文件
  const shareFiles = async () => {
    try {
      await fileShareFormRef.value.validate();
      if (fileShareForm.value.files.length === 0) {
        ElMessage.warning("请至少选择一个文件");
        return;
      }
      // 模拟文件共享
      const shareInfo = {
        title: fileShareForm.value.title,
        description: fileShareForm.value.description,
        departments: fileShareForm.value.departments,
        files: fileShareForm.value.files,
      };
      addFileSharing({ ...shareInfo })
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("文件共享成功");
            fileShareDialogVisible.value = false;
            getList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
      // ElMessage.success(`文件共享成功!共享ID: ${shareInfo.shareId},已通知相关部门`);
      // 将文件共享信息添加到通知列表
      const fileShareNotification = {
        title: `[文件共享] ${shareInfo.title}`,
        type: "temporary",
        priority: "medium",
        status: "published",
        content: `共享描述: ${shareInfo.description},文件数量: ${shareInfo.files.length}个`,
        departments: shareInfo.departments,
        expireDate: "",
        syncMethods: ["wechat", "dingtalk"],
      };
      addNotification({ ...fileShareNotification })
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("添加成功");
            // dialogVisible.value = false;
            getList();
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
      // mockData.unshift(fileShareNotification);
      // getList();
    } catch (error) {
      console.error("文件共享表单验证失败:", error);
    }
  };
  // 发布通知
  const publishNotification = row => {
    Object.assign(form.value, {
      id: row.id,
      title: row.title,
@@ -859,344 +1207,106 @@
      departments: row.departments || [],
      expireDate: row.expireDate || "",
      status: row.status,
      syncMethods: row.syncMethods || []
      syncMethods: row.syncMethods || [],
    });
  }
  dialogVisible.value = true;
};
// 打开在线会议弹窗
const openMeetingDialog = () => {
  // 重置表单
  Object.assign(meetingForm.value, {
    title: "",
    startTime: "",
    duration: 60,
    participants: [],
    description: "",
    platform: "wechat"
  });
  meetingDialogVisible.value = true;
};
// 打开文件共享弹窗
const openFileShareDialog = () => {
  // 重置表单
  Object.assign(fileShareForm.value, {
    title: "",
    description: "",
    departments: [],
    files: []
  });
  fileList.value = [];
  fileShareDialogVisible.value = true;
};
// 手动刷新数据
const manualRefresh = () => {
  generateNewData();
  getList();
  ElMessage.success("手动刷新完成,已生成新通知");
};
// 提交通知表单
const submitForm = async () => {
  try {
    await formRef.value.validate();
    if (dialogType.value === "add") {
      // 新增通知
      addNotification({...form.value}).then(res => {
        if(res.code == 200){
          ElMessage.success("添加成功");
          dialogVisible.value = false;
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
    } else {
      // 编辑通知
      updateNotification({...form.value}).then(res => {
        if(res.code == 200){
          ElMessage.success("更新成功");
          dialogVisible.value = false;
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
    }
  } catch (error) {
    console.error("表单验证失败:", error);
  }
};
// 创建会议
const createMeeting = async () => {
  try {
    await meetingFormRef.value.validate();
    // 模拟创建会议
    const meetingInfo = {
      title: meetingForm.value.title,
      startTime: meetingForm.value.startTime,
      duration: meetingForm.value.duration,
      participants: meetingForm.value.participants,
      description: meetingForm.value.description,
      platform: meetingForm.value.platform
    };
    // 新增会议
    addOnlineMeeting({...meetingInfo}).then(res => {
      if(res.code == 200){
        ElMessage.success("会议添加成功");
        meetingDialogVisible.value = false;
        getList();
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
    // 模拟发送到企业微信/钉钉
    // const platformName = meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "未知平台";
    // ElMessage.success(`会议创建成功!会议ID: ${meetingInfo.meetingId},将通过${platformName}发送通知`);
    // 获取参会人员信息
     const participantNames = meetingForm.value.participants.map(participantId => {
       const employee = employees.value.find(emp => emp.value === participantId);
       return employee ? employee.label : '未知人员';
     }).join('、');
     // 获取参会人员详细信息
     const participantDetails = meetingForm.value.participants.map(participantId => {
       const employee = employees.value.find(emp => emp.value === participantId);
       return employee ? {
         name: employee.label,
         dept: employee.dept,
         phone: employee.phone,
         email: employee.email
       } : null;
     }).filter(Boolean);
    // 将会议信息添加到通知列表
    const meetingNotification = {
      title: `[会议通知] ${meetingInfo.title}`,
      type: "meeting",
      priority: "high",
      status: "published",
      content: `会议时间: ${meetingInfo.startTime},时长: ${meetingInfo.duration}分钟,平台: ${meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "未知平台"},参会人员: ${participantNames},共${participantDetails.length}人`,
      departments: [],
      expireDate: "",
      syncMethods: [meetingForm.value.platform]
    };
    addNotification({...meetingNotification}).then(res => {
        if(res.code == 200){
          ElMessage.success("添加成功");
          // dialogVisible.value = false;
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
    // mockData.unshift(meetingNotification);
    // getList();
  } catch (error) {
    console.error("会议表单验证失败:", error);
  }
};
// 文件上传处理
const handleFileChange = (file) => {
  const isLt10M = file.size / 1024 / 1024 < 10;
  if (!isLt10M) {
    ElMessage.error("上传文件大小不能超过 10MB!");
    return false;
  }
  const fileInfo = {
    name: file.name,
    size: file.size,
    type: file.type,
    uid: file.uid
  };
  fileList.value.push(fileInfo);
  fileShareForm.value.files.push(fileInfo.name);
  return false; // 阻止自动上传
};
// 移除文件
const removeFile = (file) => {
  const index = fileList.value.findIndex(item => item.uid === file.uid);
  if (index !== -1) {
    const index2 = fileShareForm.value.files.findIndex(item => item.uid === file.uid);
    if (index2 !== -1) {
      fileShareForm.value.files.splice(index2, 1);
    }
    fileList.value.splice(index, 1);
  }
};
// 共享文件
const shareFiles = async () => {
  try {
    await fileShareFormRef.value.validate();
    if (fileShareForm.value.files.length === 0) {
      ElMessage.warning("请至少选择一个文件");
      return;
    }
    // 模拟文件共享
    const shareInfo = {
      title: fileShareForm.value.title,
      description: fileShareForm.value.description,
      departments: fileShareForm.value.departments,
      files: fileShareForm.value.files,
    };
    addFileSharing({...shareInfo}).then(res => {
      if(res.code == 200){
        ElMessage.success("文件共享成功");
        fileShareDialogVisible.value = false;
        getList();
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
    // ElMessage.success(`文件共享成功!共享ID: ${shareInfo.shareId},已通知相关部门`);
    // 将文件共享信息添加到通知列表
    const fileShareNotification = {
      title: `[文件共享] ${shareInfo.title}`,
      type: "temporary",
      priority: "medium",
      status: "published",
      content: `共享描述: ${shareInfo.description},文件数量: ${shareInfo.files.length}个`,
      departments: shareInfo.departments,
      expireDate: "",
      syncMethods: ["wechat", "dingtalk"],
    };
    addNotification({...fileShareNotification}).then(res => {
      if(res.code == 200){
        ElMessage.success("添加成功");
        // dialogVisible.value = false;
        getList();
      }
    }).catch(err => {
      ElMessage.error(err.msg);
    })
    // mockData.unshift(fileShareNotification);
    // getList();
  } catch (error) {
    console.error("文件共享表单验证失败:", error);
  }
};
// 发布通知
const publishNotification = (row) => {
  Object.assign(form.value, {
    id: row.id,
    title: row.title,
    type: row.type,
    priority: row.priority,
    content: row.content || "",
    departments: row.departments || [],
    expireDate: row.expireDate || "",
    status: row.status,
    syncMethods: row.syncMethods || []
  });
  form.value.status = "published";
  updateNotification({...form.value}).then(res => {
        if(res.code == 200){
    form.value.status = "published";
    updateNotification({ ...form.value })
      .then(res => {
        if (res.code == 200) {
          ElMessage.success("通知发布成功");
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
};
      .catch(err => {
        ElMessage.error(err.msg);
      });
  };
// 撤回通知
const revokeNotification = (row) => {
  // 撤回通知
  const revokeNotification = row => {
    Object.assign(form.value, {
    id: row.id,
    title: row.title,
    type: row.type,
    priority: row.priority,
    content: row.content || "",
    departments: row.departments || [],
    expireDate: row.expireDate || "",
    status: row.status,
    syncMethods: row.syncMethods || []
  });
  form.value.status = "draft";
  updateNotification({...form.value}).then(res => {
        if(res.code == 200){
      id: row.id,
      title: row.title,
      type: row.type,
      priority: row.priority,
      content: row.content || "",
      departments: row.departments || [],
      expireDate: row.expireDate || "",
      status: row.status,
      syncMethods: row.syncMethods || [],
    });
    form.value.status = "draft";
    updateNotification({ ...form.value })
      .then(res => {
        if (res.code == 200) {
          ElMessage.success("通知已撤回");
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
};
      .catch(err => {
        ElMessage.error(err.msg);
      });
  };
// 删除通知
const handleDelete = () => {
  let ids = [];
  if (selectedIds.value.length > 0) {
    ids = selectedIds.value;
  }else{
    ElMessage.warning("请选择要删除的通知");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    delNotification(ids).then(res => {
      if(res.code == 200){
        ElMessage.success("删除成功");
        selectedIds.value = [];
        getList();
      }
    }).catch(err => {
      ElMessage.error(err.msg);
  // 删除通知
  const handleDelete = () => {
    let ids = [];
    if (selectedIds.value.length > 0) {
      ids = selectedIds.value;
    } else {
      ElMessage.warning("请选择要删除的通知");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
  }).catch(() => {
    // 用户取消
  });
};
      .then(() => {
        delNotification(ids)
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("删除成功");
              selectedIds.value = [];
              getList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      })
      .catch(() => {
        // 用户取消
      });
  };
</script>
<style scoped>
.auto-refresh-info {
  margin-bottom: 15px;
}
  .auto-refresh-info {
    margin-bottom: 15px;
  }
.auto-refresh-info .el-alert {
  border-radius: 8px;
}
  .auto-refresh-info .el-alert {
    border-radius: 8px;
  }
.dialog-footer {
  text-align: right;
}
  .dialog-footer {
    text-align: right;
  }
.el-upload__tip {
  color: #909399;
  font-size: 12px;
  margin-top: 8px;
}
  .el-upload__tip {
    color: #909399;
    font-size: 12px;
    margin-top: 8px;
  }
.el-checkbox-group {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
  .el-checkbox-group {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
  }
.el-checkbox {
  margin-right: 0;
}
  .el-checkbox {
    margin-right: 0;
  }
</style>
src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue
@@ -3,139 +3,174 @@
    <!-- 页面标题 -->
    <div class="page-header">
      <h2>会议草稿</h2>
      <el-button type="primary" @click="handleAdd">
        <el-icon><Plus /></el-icon>
      <el-button type="primary"
                 @click="handleAdd">
        <el-icon>
          <Plus />
        </el-icon>
        新建草稿
      </el-button>
    </div>
    <!-- 搜索区域 -->
    <el-card class="search-card">
      <el-form :model="searchForm" label-width="100px" inline>
      <el-form :model="searchForm"
               label-width="100px"
               inline>
        <el-form-item label="会议主题">
          <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable />
          <el-input v-model="searchForm.title"
                    placeholder="请输入会议主题"
                    clearable />
        </el-form-item>
        <el-form-item label="会议日期">
          <el-date-picker
            v-model="searchForm.meetingDate"
            type="date"
            placeholder="请选择会议日期"
            value-format="YYYY-MM-DD"
            format="YYYY-MM-DD"
            style="width: 100%"
          />
          <el-date-picker v-model="searchForm.meetingDate"
                          type="date"
                          placeholder="请选择会议日期"
                          value-format="YYYY-MM-DD"
                          format="YYYY-MM-DD"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleSearch">搜索</el-button>
          <el-button type="primary"
                     @click="handleSearch">搜索</el-button>
          <el-button @click="resetSearch">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <!-- 草稿列表 -->
    <el-card>
      <el-table v-loading="loading" :data="draftList" border>
        <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip />
        <el-table-column prop="room" label="会议室" align="center" width="120" />
        <el-table-column prop="host" label="主持人" align="center" width="120" />
        <el-table-column prop="meetingTime" label="会议时间" align="center" width="180">
      <el-table v-loading="loading"
                :data="draftList"
                border>
        <el-table-column prop="title"
                         label="会议主题"
                         align="center"
                         min-width="200"
                         show-overflow-tooltip />
        <el-table-column prop="room"
                         label="会议室"
                         align="center"
                         width="120" />
        <el-table-column prop="host"
                         label="主持人"
                         align="center"
                         width="120" />
        <el-table-column prop="meetingTime"
                         label="会议时间"
                         align="center"
                         width="180">
          <template #default="scope">
            {{ formatDateTime(scope.row.meetingTime) }}
          </template>
        </el-table-column>
        <el-table-column prop="participants" label="参会人数" align="center" width="100">
        <el-table-column prop="participants"
                         label="参会人数"
                         align="center"
                         width="100">
          <template #default="scope">
            {{ scope.row.participants }}人
          </template>
        </el-table-column>
        <el-table-column prop="createTime" label="创建时间" align="center" width="180" />
        <el-table-column label="操作" align="center" width="200" fixed="right">
        <el-table-column prop="createTime"
                         label="创建时间"
                         align="center"
                         width="180" />
        <el-table-column label="操作"
                         align="center"
                         width="200"
                         fixed="right">
          <template #default="scope">
            <el-button type="primary" link @click="viewDraft(scope.row)">查看</el-button>
            <el-button type="primary" link @click="editDraft(scope.row)">编辑</el-button>
            <el-button type="danger" link @click="deleteDraft(scope.row)">删除</el-button>
            <el-button type="primary"
                       link
                       @click="viewDraft(scope.row)">查看</el-button>
            <el-button type="primary"
                       link
                       @click="editDraft(scope.row)">编辑</el-button>
            <el-button type="danger"
                       link
                       @click="deleteDraft(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.current"
        v-model:limit="queryParams.size"
        @pagination="getList"
      />
      <pagination v-show="total > 0"
                  :total="total"
                  v-model:page="queryParams.current"
                  v-model:limit="queryParams.size"
                  @pagination="getList" />
    </el-card>
    <!-- 会议草稿详情对话框 -->
    <el-dialog
      title="会议草稿详情"
      v-model="detailDialogVisible"
      width="800px"
    >
    <el-dialog title="会议草稿详情"
               v-model="detailDialogVisible"
               width="800px">
      <div v-if="currentDraft">
        <el-descriptions :column="2" border>
        <el-descriptions :column="2"
                         border>
          <el-descriptions-item label="会议主题">{{ currentDraft.title }}</el-descriptions-item>
          <el-descriptions-item label="会议编号">{{ currentDraft.meetingId }}</el-descriptions-item>
          <el-descriptions-item label="会议室">{{ currentDraft.room }}</el-descriptions-item>
          <el-descriptions-item label="主持人">{{ currentDraft.host }}</el-descriptions-item>
          <el-descriptions-item label="会议时间" :span="2">
          <el-descriptions-item label="会议时间"
                                :span="2">
            {{ formatDateTime(currentDraft.meetingTime) }}
          </el-descriptions-item>
          <el-descriptions-item label="创建时间">{{ currentDraft.createTime }}</el-descriptions-item>
        </el-descriptions>
        <div class="content-section mt-20">
          <h4>参会人员</h4>
          <div class="participants-list">
            {{ currentDraft.participantList }}
          </div>
        </div>
        <div class="content-section mt-20">
          <h4>会议说明</h4>
          <div class="meeting-description">{{ currentDraft.description }}</div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="detailDialogVisible = false">关 闭</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 新建/编辑草稿对话框 -->
    <el-dialog
      :title="dialogTitle"
      v-model="editDialogVisible"
      width="700px"
    >
      <el-form :model="meetingForm" :rules="rules" ref="meetingFormRef" label-width="100px">
        <el-form-item label="会议主题" prop="title">
          <el-input v-model="meetingForm.title" placeholder="请输入会议主题" />
    <el-dialog :title="dialogTitle"
               v-model="editDialogVisible"
               width="700px">
      <el-form :model="meetingForm"
               :rules="rules"
               ref="meetingFormRef"
               label-width="100px">
        <el-form-item label="会议主题"
                      prop="title">
          <el-input v-model="meetingForm.title"
                    placeholder="请输入会议主题" />
        </el-form-item>
        <el-form-item label="会议室" prop="room">
          <el-select v-model="meetingForm.roomId" placeholder="请选择会议室" style="width: 100%">
            <el-option v-for="(v,k) in roomList" :label="v.name" :value="v.id" />
        <el-form-item label="会议室"
                      prop="room">
          <el-select v-model="meetingForm.roomId"
                     placeholder="请选择会议室"
                     style="width: 100%">
            <el-option v-for="(v,k) in roomList"
                       :label="v.name"
                       :value="v.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="主持人" prop="host">
          <el-input v-model="meetingForm.host" placeholder="请输入主持人" />
        <el-form-item label="主持人"
                      prop="host">
          <el-input v-model="meetingForm.host"
                    placeholder="请输入主持人" />
        </el-form-item>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="会议日期" prop="meetingDate">
              <el-date-picker
                v-model="meetingForm.meetingDate"
                type="date"
                placeholder="请选择会议日期"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                :disabled-date="disabledDate"
                style="width: 100%"
              />
            <el-form-item label="会议日期"
                          prop="meetingDate">
              <el-date-picker v-model="meetingForm.meetingDate"
                              type="date"
                              placeholder="请选择会议日期"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              :disabled-date="disabledDate"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -144,67 +179,57 @@
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="开始时间" prop="startTime">
              <el-select
                v-model="meetingForm.startTime"
                placeholder="请选择开始时间"
                style="width: 100%"
              >
                <el-option
                  v-for="time in timeOptions"
                  :key="time.value"
                  :label="time.label"
                  :value="time.value"
                />
            <el-form-item label="开始时间"
                          prop="startTime">
              <el-select v-model="meetingForm.startTime"
                         placeholder="请选择开始时间"
                         style="width: 100%">
                <el-option v-for="time in timeOptions"
                           :key="time.value"
                           :label="time.label"
                           :value="time.value" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="结束时间" prop="endTime">
              <el-select
                v-model="meetingForm.endTime"
                placeholder="请选择结束时间"
                style="width: 100%"
              >
                <el-option
                  v-for="time in timeOptions"
                  :key="time.value"
                  :label="time.label"
                  :value="time.value"
                />
            <el-form-item label="结束时间"
                          prop="endTime">
              <el-select v-model="meetingForm.endTime"
                         placeholder="请选择结束时间"
                         style="width: 100%">
                <el-option v-for="time in timeOptions"
                           :key="time.value"
                           :label="time.label"
                           :value="time.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="参会人数" prop="participants">
          <el-input
              v-model="meetingForm.participants"
              type="number"
              placeholder="请输入参会人数"
          />
        <el-form-item label="参会人数"
                      prop="participants">
          <el-input v-model="meetingForm.participants"
                    type="number"
                    placeholder="请输入参会人数" />
        </el-form-item>
        <el-form-item label="参会人员" prop="participants">
          <el-input
            v-model="meetingForm.participantList"
            type="textarea"
            :rows="3"
            placeholder="请输入参会人员,用逗号分隔"
          />
        <el-form-item label="参会人员"
                      prop="participants">
          <el-input v-model="meetingForm.participantList"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入参会人员,用逗号分隔" />
        </el-form-item>
        <el-form-item label="会议说明">
          <el-input
            v-model="meetingForm.description"
            type="textarea"
            :rows="4"
            placeholder="请输入会议说明"
          />
          <el-input v-model="meetingForm.description"
                    type="textarea"
                    :rows="4"
                    placeholder="请输入会议说明" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitForm">保 存</el-button>
          <el-button @click="editDialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="submitForm">保 存</el-button>
        </div>
      </template>
    </el-dialog>
@@ -212,284 +237,294 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import Pagination from '@/components/Pagination/index.vue'
import {getRoomEnum,getDraftList,saveDraft,delDraft} from '@/api/collaborativeApproval/meeting.js'
import dayjs from "dayjs";
// 数据列表加载状态
const loading = ref(false)
  import { ref, reactive, onMounted } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { Plus } from "@element-plus/icons-vue";
  import Pagination from "@/components/Pagination/index.vue";
  import {
    getRoomEnum,
    getDraftList,
    saveDraft,
    delDraft,
  } from "@/api/collaborativeApproval/meeting.js";
  import dayjs from "dayjs";
  // 数据列表加载状态
  const loading = ref(false);
// 总条数
const total = ref(0)
  // 总条数
  const total = ref(0);
// 草稿列表数据
const draftList = ref([])
  // 草稿列表数据
  const draftList = ref([]);
// 查询参数
const queryParams = reactive({
  current: 1,
  size: 10
})
  // 查询参数
  const queryParams = reactive({
    current: 1,
    size: 10,
  });
// 搜索表单
const searchForm = reactive({
  title: '',
  meetingDate: ''
})
  // 搜索表单
  const searchForm = reactive({
    title: "",
    meetingDate: "",
  });
// 是否显示对话框
const detailDialogVisible = ref(false)
const editDialogVisible = ref(false)
  // 是否显示对话框
  const detailDialogVisible = ref(false);
  const editDialogVisible = ref(false);
const roomList = ref([])
  const roomList = ref([]);
// 对话框标题
const dialogTitle = ref('')
  // 对话框标题
  const dialogTitle = ref("");
// 当前查看的草稿
const currentDraft = ref(null)
  // 当前查看的草稿
  const currentDraft = ref(null);
// 表单引用
const meetingFormRef = ref(null)
  // 表单引用
  const meetingFormRef = ref(null);
// 时间选项(以半小时为间隔,工作时间8:00-18:00)
const timeOptions = ref([])
  // 时间选项(以半小时为间隔,工作时间8:00-18:00)
  const timeOptions = ref([]);
// 表单数据
const meetingForm = reactive({
  id: '',
  meetingId: '',
  title: '',
  roomId: '',
  host: '',
  meetingDate: '',
  startTime: '',
  endTime: '',
  participants: 0,
  participantList: '',
  description: '',
  createTime: ''
})
// 表单校验规则
const rules = {
  title: [{ required: true, message: '请输入会议主题', trigger: 'blur' }],
  roomId: [{ required: true, message: '请选择会议室', trigger: 'change' }],
  host: [{ required: true, message: '请输入主持人', trigger: 'blur' }],
  meetingDate: [{ required: true, message: '请选择会议日期', trigger: 'change' }],
  startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
  endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
}
// 初始化时间选项(以半小时为间隔,工作时间8:00-18:00)
const initTimeOptions = () => {
  const options = []
  for (let hour = 8; hour <= 18; hour++) {
    // 每个小时添加两个选项:整点和半点
    options.push({
      value: `${hour.toString().padStart(2, '0')}:00`,
      label: `${hour.toString().padStart(2, '0')}:00`
    })
    if (hour < 18) { // 18:00之后没有半点选项
      options.push({
        value: `${hour.toString().padStart(2, '0')}:30`,
        label: `${hour.toString().padStart(2, '0')}:30`
      })
    }
  }
  timeOptions.value = options
}
// 禁用日期(禁用今天之前的日期)
const disabledDate = (time) => {
  // 禁用今天之前的日期
  return time.getTime() < Date.now() - 86400000
}
// 查询数据
const getList = async () => {
  loading.value = true
  let resp = await getDraftList({...queryParams,...searchForm})
  queryParams.current = resp.data.current
  draftList.value = resp.data.records.map(it=>{
    it.room = roomList.value.find(room=>it.roomId===room.id).name ?? ""
    it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format("HH:mm")} ~ ${dayjs(it.endTime).format("HH:mm")}`
    return it
  })
  loading.value = false
}
// 搜索按钮操作
const handleSearch = () => {
  queryParams.pageNum = 1
  getList()
}
// 重置搜索表单
const resetSearch = () => {
  Object.assign(searchForm, {
    title: '',
    createTime: []
  })
  handleSearch()
}
// 添加按钮操作
const handleAdd = () => {
  dialogTitle.value = '新建草稿'
  resetForm()
  editDialogVisible.value = true
}
// 查看草稿详情
const viewDraft = (row) => {
  currentDraft.value = row
  detailDialogVisible.value = true
}
// 编辑草稿
const editDraft = (row) => {
  dialogTitle.value = '编辑草稿'
  Object.assign(meetingForm, {
    id: row.id,
    meetingId: row.meetingId,
    title: row.title,
    room: row.room,
    roomId: row.id,
    host: row.host,
    meetingDate: row.meetingTime.split(' ')[0],
    startTime: row.meetingTime.split(' ')[1],
    endTime: row.meetingTime.split(' ')[3],
    participants: row.participants,
    participantList: row.participantList,
    description: row.description,
    createTime: row.createTime
  })
  editDialogVisible.value = true
}
// 删除草稿
const deleteDraft = (row) => {
  ElMessageBox.confirm(
    `确认删除会议草稿 "${row.title}"?`,
    '删除草稿',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }
  ).then(() => {
    delDraft(row.id).then(resp=>{
      ElMessage.success('草稿删除成功')
      getList()
    })
  }).catch(() => {})
}
// 重置表单
const resetForm = () => {
  Object.assign(meetingForm, {
    id: '',
    meetingId: '',
    title: '',
    room: '',
    host: '',
    meetingDate: '',
    startTime: '',
    endTime: '',
  // 表单数据
  const meetingForm = reactive({
    id: "",
    meetingId: "",
    title: "",
    roomId: "",
    host: "",
    meetingDate: "",
    startTime: "",
    endTime: "",
    participants: 0,
    participantList: '',
    description: '',
    createTime: ''
  })
}
    participantList: "",
    description: "",
    createTime: "",
  });
// 提交表单
const submitForm = () => {
  meetingFormRef.value.validate((valid) => {
    if (valid) {
      let formData = {...meetingForm}
      formData.startTime = dayjs(meetingForm.meetingDate + ' ' + meetingForm.startTime).format("YYYY-MM-DD HH:mm:ss")
      formData.endTime = dayjs(meetingForm.meetingDate + ' ' + meetingForm.endTime).format("YYYY-MM-DD HH:mm:ss")
      saveDraft(formData).then(()=>{
        ElMessage.success('保存成功')
        editDialogVisible.value = false
        getList()
      })
  // 表单校验规则
  const rules = {
    title: [{ required: true, message: "请输入会议主题", trigger: "blur" }],
    roomId: [{ required: true, message: "请选择会议室", trigger: "change" }],
    host: [{ required: true, message: "请输入主持人", trigger: "blur" }],
    meetingDate: [
      { required: true, message: "请选择会议日期", trigger: "change" },
    ],
    startTime: [{ required: true, message: "请选择开始时间", trigger: "change" }],
    endTime: [{ required: true, message: "请选择结束时间", trigger: "change" }],
  };
  // 初始化时间选项(以半小时为间隔,工作时间8:00-18:00)
  const initTimeOptions = () => {
    const options = [];
    for (let hour = 8; hour <= 18; hour++) {
      // 每个小时添加两个选项:整点和半点
      options.push({
        value: `${hour.toString().padStart(2, "0")}:00`,
        label: `${hour.toString().padStart(2, "0")}:00`,
      });
      if (hour < 18) {
        // 18:00之后没有半点选项
        options.push({
          value: `${hour.toString().padStart(2, "0")}:30`,
          label: `${hour.toString().padStart(2, "0")}:30`,
        });
      }
    }
  })
}
    timeOptions.value = options;
  };
// 格式化日期时间
const formatDateTime = (dateTime) => {
  if (!dateTime) return ''
  return dateTime.replace(' ', '\n')
}
  // 禁用日期(禁用今天之前的日期)
  const disabledDate = time => {
    // 禁用今天之前的日期
    return time.getTime() < Date.now() - 86400000;
  };
// 页面加载时获取数据
onMounted(() => {
  initTimeOptions()
  getList()
  getRoomEnum().then((res) => {
    roomList.value = res.data
  })
})
  // 查询数据
  const getList = async () => {
    loading.value = true;
    let resp = await getDraftList({ ...queryParams, ...searchForm });
    queryParams.current = resp.data.current;
    draftList.value = resp.data.records.map(it => {
      it.room = roomList.value.find(room => it.roomId === room.id).name ?? "";
      it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format(
        "HH:mm"
      )} ~ ${dayjs(it.endTime).format("HH:mm")}`;
      return it;
    });
    loading.value = false;
  };
  // 搜索按钮操作
  const handleSearch = () => {
    queryParams.pageNum = 1;
    getList();
  };
  // 重置搜索表单
  const resetSearch = () => {
    Object.assign(searchForm, {
      title: "",
      createTime: [],
    });
    handleSearch();
  };
  // 添加按钮操作
  const handleAdd = () => {
    dialogTitle.value = "新建草稿";
    resetForm();
    editDialogVisible.value = true;
  };
  // 查看草稿详情
  const viewDraft = row => {
    currentDraft.value = row;
    detailDialogVisible.value = true;
  };
  // 编辑草稿
  const editDraft = row => {
    dialogTitle.value = "编辑草稿";
    Object.assign(meetingForm, {
      id: row.id,
      meetingId: row.meetingId,
      title: row.title,
      room: row.room,
      roomId: row.id,
      host: row.host,
      meetingDate: row.meetingTime.split(" ")[0],
      startTime: row.meetingTime.split(" ")[1],
      endTime: row.meetingTime.split(" ")[3],
      participants: row.participants,
      participantList: row.participantList,
      description: row.description,
      createTime: row.createTime,
    });
    editDialogVisible.value = true;
  };
  // 删除草稿
  const deleteDraft = row => {
    ElMessageBox.confirm(`确认删除会议草稿 "${row.title}"?`, "删除草稿", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delDraft(row.id).then(resp => {
          ElMessage.success("草稿删除成功");
          getList();
        });
      })
      .catch(() => {});
  };
  // 重置表单
  const resetForm = () => {
    Object.assign(meetingForm, {
      id: "",
      meetingId: "",
      title: "",
      room: "",
      host: "",
      meetingDate: "",
      startTime: "",
      endTime: "",
      participants: 0,
      participantList: "",
      description: "",
      createTime: "",
    });
  };
  // 提交表单
  const submitForm = () => {
    meetingFormRef.value.validate(valid => {
      if (valid) {
        let formData = { ...meetingForm };
        formData.startTime = dayjs(
          meetingForm.meetingDate + " " + meetingForm.startTime
        ).format("YYYY-MM-DD HH:mm:ss");
        formData.endTime = dayjs(
          meetingForm.meetingDate + " " + meetingForm.endTime
        ).format("YYYY-MM-DD HH:mm:ss");
        saveDraft(formData).then(() => {
          ElMessage.success("保存成功");
          editDialogVisible.value = false;
          getList();
        });
      }
    });
  };
  // 格式化日期时间
  const formatDateTime = dateTime => {
    if (!dateTime) return "";
    return dateTime.replace(" ", "\n");
  };
  // 页面加载时获取数据
  onMounted(() => {
    initTimeOptions();
    getList();
    getRoomEnum().then(res => {
      roomList.value = res.data;
    });
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
}
  .app-container {
    padding: 20px;
  }
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}
  .page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
  }
.page-header h2 {
  margin: 0;
  color: #303133;
}
  .page-header h2 {
    margin: 0;
    color: #303133;
  }
.search-card {
  margin-bottom: 20px;
}
  .search-card {
    margin-bottom: 20px;
  }
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
  }
.content-section h4 {
  margin: 0 0 15px 0;
  color: #303133;
}
  .content-section h4 {
    margin: 0 0 15px 0;
    color: #303133;
  }
.mt-20 {
  margin-top: 20px;
}
  .mt-20 {
    margin-top: 20px;
  }
.participants-list {
  min-height: 40px;
  padding: 15px;
  border-radius: 4px;
  line-height: 1.6;
}
  .participants-list {
    min-height: 40px;
    padding: 15px;
    border-radius: 4px;
    line-height: 1.6;
  }
.meeting-description {
  padding: 15px;
  border-radius: 4px;
  line-height: 1.6;
  white-space: pre-wrap;
}
  .meeting-description {
    padding: 15px;
    border-radius: 4px;
    line-height: 1.6;
    white-space: pre-wrap;
  }
</style>
src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
@@ -4,184 +4,216 @@
    <div class="page-header">
      <h2>会议发布</h2>
    </div>
    <!-- 搜索区域 -->
    <el-card class="search-card">
      <el-form :model="searchForm" inline>
      <el-form :model="searchForm"
               inline>
        <el-form-item label="会议主题">
          <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable/>
          <el-input v-model="searchForm.title"
                    placeholder="请输入会议主题"
                    clearable />
        </el-form-item>
        <el-form-item label="申请人">
          <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable/>
          <el-input v-model="searchForm.applicant"
                    placeholder="请输入申请人"
                    clearable />
        </el-form-item>
        <el-form-item label="发布状态">
          <el-select style="width: 100px" v-model="searchForm.status" placeholder="请选择发布状态" clearable>
            <el-option label="待发布" value="0"/>
            <el-option label="已发布" value="1"/>
          <el-select style="width: 100px"
                     v-model="searchForm.status"
                     placeholder="请选择发布状态"
                     clearable>
            <el-option label="待发布"
                       value="0" />
            <el-option label="已发布"
                       value="1" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleSearch">搜索</el-button>
          <el-button type="primary"
                     @click="handleSearch">搜索</el-button>
          <el-button @click="resetSearch">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <!-- 会议发布列表 -->
    <el-card>
      <el-table v-loading="loading" :data="approvalList" border>
        <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip/>
        <el-table-column prop="applicant" label="申请人" align="center" width="120"/>
        <el-table-column prop="host" label="主理人" align="center" width="120"/>
        <el-table-column prop="meetingTime" label="会议时间" align="center" width="180">
      <el-table v-loading="loading"
                :data="approvalList"
                border>
        <el-table-column prop="title"
                         label="会议主题"
                         align="center"
                         min-width="200"
                         show-overflow-tooltip />
        <el-table-column prop="applicant"
                         label="申请人"
                         align="center"
                         width="120" />
        <el-table-column prop="host"
                         label="主理人"
                         align="center"
                         width="120" />
        <el-table-column prop="meetingTime"
                         label="会议时间"
                         align="center"
                         width="180">
          <template #default="scope">
            {{ formatDateTime(scope.row.meetingTime) }}
          </template>
        </el-table-column>
        <el-table-column prop="location" label="会议地点" align="center" width="150"/>
        <el-table-column prop="participants" label="参会人数" align="center" width="100">
        <el-table-column prop="location"
                         label="会议地点"
                         align="center"
                         width="150" />
        <el-table-column prop="participants"
                         label="参会人数"
                         align="center"
                         width="100">
          <template #default="scope">
            {{ scope.row.participants.length }}人
          </template>
        </el-table-column>
        <el-table-column prop="status" label="发布状态" align="center" width="120">
        <el-table-column prop="status"
                         label="发布状态"
                         align="center"
                         width="120">
          <template #default="scope">
            <el-tag :type="getStatusType(scope.row.status)">
              {{ getStatusText(scope.row.status) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" align="center" width="200" fixed="right">
        <el-table-column label="操作"
                         align="center"
                         width="200"
                         fixed="right">
          <template #default="scope">
            <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button>
            <el-button
                v-if="scope.row.status == '0'"
                type="primary"
                link
                @click="handleApproval(scope.row)"
            >
            <el-button type="primary"
                       link
                       @click="viewDetail(scope.row)">查看</el-button>
            <el-button v-if="scope.row.status == '0'"
                       type="primary"
                       link
                       @click="handleApproval(scope.row)">
              发布
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <pagination
          v-show="total > 0"
          :total="total"
          v-model:page="queryParams.current"
          v-model:limit="queryParams.size"
          @pagination="getList"
      />
      <pagination v-show="total > 0"
                  :total="total"
                  v-model:page="queryParams.current"
                  v-model:limit="queryParams.size"
                  @pagination="getList" />
    </el-card>
    <!-- 会议详情对话框 -->
    <el-dialog
        title="会议详情"
        v-model="detailDialogVisible"
        width="800px"
    >
    <el-dialog title="会议详情"
               v-model="detailDialogVisible"
               width="800px">
      <div v-if="currentMeeting">
         <el-descriptions label-width="100px" class="meeting-desc" :column="2" border>
          <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{
        <el-descriptions label-width="100px"
                         class="meeting-desc"
                         :column="2"
                         border>
          <el-descriptions-item label="会议主题"
                                label-class-name="nowrap-label">{{
              currentMeeting.title
            }}</el-descriptions-item>
          <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{
          <el-descriptions-item label="申请人"
                                label-class-name="nowrap-label">{{
              currentMeeting.applicant
            }}</el-descriptions-item>
          <el-descriptions-item label="主理人" label-class-name="nowrap-label">{{
          <el-descriptions-item label="主理人"
                                label-class-name="nowrap-label">{{
              currentMeeting.host
            }}</el-descriptions-item>
          <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label">
          <el-descriptions-item label="会议时间"
                                :span="2"
                                label-class-name="nowrap-label">
            {{ formatDateTime(currentMeeting.meetingTime) }}
          </el-descriptions-item>
          <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{
          <el-descriptions-item label="会议地点"
                                label-class-name="nowrap-label">{{
              currentMeeting.location
            }}</el-descriptions-item>
          <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{
          <el-descriptions-item label="参会人数"
                                label-class-name="nowrap-label">{{
              currentMeeting.participants.length
            }}人</el-descriptions-item>
          <el-descriptions-item label="发布状态" label-class-name="nowrap-label">
          <el-descriptions-item label="发布状态"
                                label-class-name="nowrap-label">
            <el-tag :type="getStatusType(currentMeeting.status)">
              {{ getStatusText(currentMeeting.status) }}
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{
          <el-descriptions-item label="申请时间"
                                label-class-name="nowrap-label">{{
              currentMeeting.createTime
            }}</el-descriptions-item>
          <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2"
          <el-descriptions-item style="max-height: 400px"
                                label="会议说明"
                                :span="2"
                                label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item>
        </el-descriptions>
        <div class="content-section mt-20">
          <h4>参会人员</h4>
          <div class="participants-list">
            <el-tag
                v-for="participant in currentMeeting.participants"
                :key="participant.id"
                style="margin-right: 10px; margin-bottom: 10px;"
            >
            <el-tag v-for="participant in currentMeeting.participants"
                    :key="participant.id"
                    style="margin-right: 10px; margin-bottom: 10px;">
              {{ participant.name }}
            </el-tag>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="detailDialogVisible = false">关 闭</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 会议发布对话框 -->
    <el-dialog
        title="会议发布"
        v-model="approvalDialogVisible"
    >
    <el-dialog title="会议发布"
               v-model="approvalDialogVisible">
      <div v-if="currentMeeting">
        <el-descriptions :column="2" border>
        <el-descriptions :column="2"
                         border>
          <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item>
          <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item>
          <el-descriptions-item label="主理人">{{ currentMeeting.host }}</el-descriptions-item>
          <el-descriptions-item label="会议时间" :span="2">
          <el-descriptions-item label="会议时间"
                                :span="2">
            {{ formatDateTime(currentMeeting.meetingTime) }}
          </el-descriptions-item>
          <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item>
          <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item>
        </el-descriptions>
        <div class="content-section mt-20">
          <h4>参会人员</h4>
          <div class="participants-list">
            <el-tag
                v-for="participant in currentMeeting.participants"
                :key="participant.id"
                style="margin-right: 10px; margin-bottom: 10px;"
            >
            <el-tag v-for="participant in currentMeeting.participants"
                    :key="participant.id"
                    style="margin-right: 10px; margin-bottom: 10px;">
              {{ participant.name }}
            </el-tag>
          </div>
        </div>
        <div class="approval-opinion mt-20">
          <h4>发布意见</h4>
          <el-input
              v-model="publishComment"
              type="textarea"
              placeholder="请输入发布意见"
              :rows="4"
          />
          <el-input v-model="publishComment"
                    type="textarea"
                    placeholder="请输入发布意见"
                    :rows="4" />
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitApproval('1')">发 布</el-button>
          <el-button @click="approvalDialogVisible = false">取 消</el-button>
<!--          <el-button type="danger" @click="submitApproval('2')">不通过</el-button>-->
          <el-button type="primary" @click="submitApproval('1')">发 布</el-button>
          <!--          <el-button type="danger" @click="submitApproval('2')">不通过</el-button>-->
        </div>
      </template>
    </el-dialog>
@@ -189,228 +221,235 @@
</template>
<script setup>
import {ref, reactive, onMounted} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import Pagination from '@/components/Pagination/index.vue'
import {getRoomEnum, getMeetingPublish,saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js'
import {getStaffOnJob} from "@/api/personnelManagement/onboarding.js";
import dayjs from "dayjs";
  import { ref, reactive, onMounted } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import Pagination from "@/components/Pagination/index.vue";
  import {
    getRoomEnum,
    getMeetingPublish,
    saveMeetingApplication,
  } from "@/api/collaborativeApproval/meeting.js";
  import { getStaffOnJob } from "@/api/personnelManagement/onboarding.js";
  import dayjs from "dayjs";
// 数据列表加载状态
const loading = ref(false)
  // 数据列表加载状态
  const loading = ref(false);
// 总条数
const total = ref(0)
const roomEnum = ref([])
const staffList = ref([])
// 发布列表数据
const approvalList = ref([])
  // 总条数
  const total = ref(0);
  const roomEnum = ref([]);
  const staffList = ref([]);
  // 发布列表数据
  const approvalList = ref([]);
// 查询参数
const queryParams = reactive({
  current: 1,
  size: 10
})
  // 查询参数
  const queryParams = reactive({
    current: 1,
    size: 10,
  });
// 搜索表单
const searchForm = reactive({
  title: '',
  applicant: '',
  status: ''
})
  // 搜索表单
  const searchForm = reactive({
    title: "",
    applicant: "",
    status: "",
  });
// 是否显示对话框
const detailDialogVisible = ref(false)
const approvalDialogVisible = ref(false)
  // 是否显示对话框
  const detailDialogVisible = ref(false);
  const approvalDialogVisible = ref(false);
// 当前查看的会议
const currentMeeting = ref(null)
  // 当前查看的会议
  const currentMeeting = ref(null);
// 发布意见
const publishComment = ref('')
  // 发布意见
  const publishComment = ref("");
// 查询数据
const getList = async () => {
  loading.value = true
  let resp = await getMeetingPublish({...searchForm, ...queryParams})
  approvalList.value = resp.data.records.map(it => {
    let room = roomEnum.value.find(room => it.roomId === room.id)
    it.location = `${room.name}(${room.location})`
    let staffs = JSON.parse(it.participants)
    it.staffCount = staffs.size
    it.status = it.publishStatus
    it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}`
    it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => {
      return {
        id: staff.id,
        name: `${staff.staffName}(${staff.postJob})`
      }
    })
  // 查询数据
  const getList = async () => {
    loading.value = true;
    let resp = await getMeetingPublish({ ...searchForm, ...queryParams });
    approvalList.value = resp.data.records.map(it => {
      let room = roomEnum.value.find(room => it.roomId === room.id);
      it.location = `${room.name}(${room.location})`;
      let staffs = JSON.parse(it.participants);
      it.staffCount = staffs.size;
      it.status = it.publishStatus;
      it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format(
        "HH:mm:ss"
      )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`;
      it.participants = staffList.value
        .filter(staff => staffs.some(id => id === staff.id))
        .map(staff => {
          return {
            id: staff.id,
            name: `${staff.staffName}(${staff.postJob})`,
          };
        });
      return it;
    });
    total.value = resp.data.total;
    loading.value = false;
  };
    return it
  })
  total.value = resp.data.total
  loading.value = false
}
  // 搜索按钮操作
  const handleSearch = () => {
    queryParams.pageNum = 1;
    getList();
  };
// 搜索按钮操作
const handleSearch = () => {
  queryParams.pageNum = 1
  getList()
}
  // 重置搜索表单
  const resetSearch = () => {
    Object.assign(searchForm, {
      title: "",
      applicant: "",
      status: "",
    });
    handleSearch();
  };
// 重置搜索表单
const resetSearch = () => {
  Object.assign(searchForm, {
    title: '',
    applicant: '',
    status: ''
  })
  handleSearch()
}
  // 查看详情
  const viewDetail = row => {
    currentMeeting.value = row;
    detailDialogVisible.value = true;
  };
// 查看详情
const viewDetail = (row) => {
  currentMeeting.value = row
  detailDialogVisible.value = true
}
  // 处理发布
  const handleApproval = row => {
    currentMeeting.value = row;
    publishComment.value = "";
    approvalDialogVisible.value = true;
  };
// 处理发布
const handleApproval = (row) => {
  currentMeeting.value = row
  publishComment.value = ''
  approvalDialogVisible.value = true
}
  // 获取状态类型
  const getStatusType = status => {
    const statusMap = {
      0: "info", // 待发布
      1: "success", // 已通过
      2: "danger", // 未通过
    };
    return statusMap[status] || "info";
  };
// 获取状态类型
const getStatusType = (status) => {
  const statusMap = {
    '0': 'info',     // 待发布
    '1': 'success',  // 已通过
    '2': 'danger',  // 未通过
  }
  return statusMap[status] || 'info'
}
  // 获取状态文本
  const getStatusText = status => {
    const statusMap = {
      0: "待发布",
      1: "已发布",
      2: "已取消",
    };
    return statusMap[status] || "未知";
  };
// 获取状态文本
const getStatusText = (status) => {
  const statusMap = {
    '0': '待发布',
    '1': '已发布',
    '2': '已取消',
  }
  return statusMap[status] || '未知'
}
  // 格式化日期时间
  const formatDateTime = dateTime => {
    if (!dateTime) return "";
    return dateTime.replace(" ", "\n");
  };
// 格式化日期时间
const formatDateTime = (dateTime) => {
  if (!dateTime) return ''
  return dateTime.replace(' ', '\n')
}
  // 提交发布
  const submitApproval = status => {
    // if (status === 'approved' && !publishComment.value.trim()) {
    //   ElMessage.warning('请填写发布意见')
    //   return
    // }
// 提交发布
const submitApproval = (status) => {
  // if (status === 'approved' && !publishComment.value.trim()) {
  //   ElMessage.warning('请填写发布意见')
  //   return
  // }
  ElMessageBox.confirm(
      `确认${status === '1' ? '发布' : '取消'}该会议?`,
      '发布确认',
    ElMessageBox.confirm(
      `确认${status === "1" ? "发布" : "取消"}该会议?`,
      "发布确认",
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }
  ).then(() => {
    saveMeetingApplication({
      id: currentMeeting.value.id,
      publishStatus: status,
      publishComment: publishComment.value
    }).then(resp=>{
      // 更新会议状态
      currentMeeting.value.status = status
    )
      .then(() => {
        saveMeetingApplication({
          id: currentMeeting.value.id,
          publishStatus: status,
          publishComment: publishComment.value,
        }).then(resp => {
          // 更新会议状态
          currentMeeting.value.status = status;
      ElMessage.success('发布提交成功')
      approvalDialogVisible.value = false
      getList()
    })
          ElMessage.success("发布提交成功");
          approvalDialogVisible.value = false;
          getList();
        });
      })
      .catch(() => {});
  };
  }).catch(() => {
  })
}
  // 页面加载时获取数据
  onMounted(async () => {
    const [resp1, resp2] = await Promise.all([getRoomEnum(), getStaffOnJob()]);
    roomEnum.value = resp1.data;
    staffList.value = resp2.data;
// 页面加载时获取数据
onMounted(async () => {
  const [resp1, resp2]= await Promise.all([getRoomEnum(), getStaffOnJob()])
  roomEnum.value = resp1.data
  staffList.value = resp2.data
  await getList()
})
    await getList();
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
}
  .app-container {
    padding: 20px;
  }
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}
  .page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
  }
.page-header h2 {
  margin: 0;
  color: #303133;
}
  .page-header h2 {
    margin: 0;
    color: #303133;
  }
.search-card {
  margin-bottom: 20px;
}
  .search-card {
    margin-bottom: 20px;
  }
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
  }
.content-section h4 {
  margin: 0 0 15px 0;
  color: #303133;
}
  .content-section h4 {
    margin: 0 0 15px 0;
    color: #303133;
  }
.mt-20 {
  margin-top: 20px;
}
  .mt-20 {
    margin-top: 20px;
  }
.participants-list {
  min-height: 40px;
  padding: 15px;
  border-radius: 4px;
  line-height: 1.6;
}
  .participants-list {
    min-height: 40px;
    padding: 15px;
    border-radius: 4px;
    line-height: 1.6;
  }
.approval-opinion h4 {
  margin: 0 0 15px 0;
  color: #303133;
}
  .approval-opinion h4 {
    margin: 0 0 15px 0;
    color: #303133;
  }
.nowrap-label {
  white-space: nowrap !important;
}
  .nowrap-label {
    white-space: nowrap !important;
  }
.description-content {
  white-space: pre-wrap;
  word-wrap: break-word;
  line-height: 1.6;
  padding: 10px;
  background-color: #f5f7fa;
  border-radius: 4px;
  min-height: 60px;
}
  .description-content {
    white-space: pre-wrap;
    word-wrap: break-word;
    line-height: 1.6;
    padding: 10px;
    background-color: #f5f7fa;
    border-radius: 4px;
    min-height: 60px;
  }
</style>
src/views/collaborativeApproval/notificationManagement/summary/index.vue
@@ -4,157 +4,185 @@
    <div class="page-header">
      <h2>会议纪要</h2>
    </div>
    <!-- 搜索区域 -->
    <el-card class="search-card">
      <el-form :model="searchForm" inline>
      <el-form :model="searchForm"
               inline>
        <el-form-item label="会议主题">
          <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable />
          <el-input v-model="searchForm.title"
                    placeholder="请输入会议主题"
                    clearable />
        </el-form-item>
        <el-form-item label="申请人">
          <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable />
          <el-input v-model="searchForm.applicant"
                    placeholder="请输入申请人"
                    clearable />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleSearch">搜索</el-button>
          <el-button type="primary"
                     @click="handleSearch">搜索</el-button>
          <el-button @click="resetSearch">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <!-- 会议列表 -->
    <el-card>
      <el-table v-loading="loading" :data="meetingList" border>
        <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip />
        <el-table-column prop="applicant" label="申请人" align="center" width="120" />
        <el-table-column prop="host" label="主持人" align="center" width="120" />
        <el-table-column prop="meetingTime" label="会议时间" align="center" width="180">
      <el-table v-loading="loading"
                :data="meetingList"
                border>
        <el-table-column prop="title"
                         label="会议主题"
                         align="center"
                         min-width="200"
                         show-overflow-tooltip />
        <el-table-column prop="applicant"
                         label="申请人"
                         align="center"
                         width="120" />
        <el-table-column prop="host"
                         label="主持人"
                         align="center"
                         width="120" />
        <el-table-column prop="meetingTime"
                         label="会议时间"
                         align="center"
                         width="180">
          <template #default="scope">
            {{ formatDateTime(scope.row.meetingTime) }}
          </template>
        </el-table-column>
        <el-table-column prop="location" label="会议地点" align="center" width="150" />
        <el-table-column prop="participants" label="参会人数" align="center" width="100">
        <el-table-column prop="location"
                         label="会议地点"
                         align="center"
                         width="150" />
        <el-table-column prop="participants"
                         label="参会人数"
                         align="center"
                         width="100">
          <template #default="scope">
            {{ scope.row.participants.length }}人
          </template>
        </el-table-column>
        <el-table-column label="操作" align="center" width="200" fixed="right">
        <el-table-column label="操作"
                         align="center"
                         width="200"
                         fixed="right">
          <template #default="scope">
            <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button>
            <el-button
              type="primary"
              link
              @click="addMinutes(scope.row)"
            >
            <el-button type="primary"
                       link
                       @click="viewDetail(scope.row)">查看</el-button>
            <el-button type="primary"
                       link
                       @click="addMinutes(scope.row)">
              添加纪要
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.current"
        v-model:limit="queryParams.size"
        @pagination="getList"
      />
      <pagination v-show="total > 0"
                  :total="total"
                  v-model:page="queryParams.current"
                  v-model:limit="queryParams.size"
                  @pagination="getList" />
    </el-card>
    <!-- 会议详情对话框 -->
    <el-dialog
      title="会议详情"
      v-model="detailDialogVisible"
      width="800px"
    >
    <el-dialog title="会议详情"
               v-model="detailDialogVisible"
               width="800px">
      <div v-if="currentMeeting">
        <el-descriptions label-width="100px" class="meeting-desc" :column="2" border>
          <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{
        <el-descriptions label-width="100px"
                         class="meeting-desc"
                         :column="2"
                         border>
          <el-descriptions-item label="会议主题"
                                label-class-name="nowrap-label">{{
            currentMeeting.title
          }}</el-descriptions-item>
          <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{
          <el-descriptions-item label="申请人"
                                label-class-name="nowrap-label">{{
            currentMeeting.applicant
          }}</el-descriptions-item>
          <el-descriptions-item label="主持人" label-class-name="nowrap-label">{{
          <el-descriptions-item label="主持人"
                                label-class-name="nowrap-label">{{
            currentMeeting.host
          }}</el-descriptions-item>
          <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label">
          <el-descriptions-item label="会议时间"
                                :span="2"
                                label-class-name="nowrap-label">
            {{ formatDateTime(currentMeeting.meetingTime) }}
          </el-descriptions-item>
          <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{
          <el-descriptions-item label="会议地点"
                                label-class-name="nowrap-label">{{
            currentMeeting.location
          }}</el-descriptions-item>
          <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{
          <el-descriptions-item label="参会人数"
                                label-class-name="nowrap-label">{{
            currentMeeting.participants.length
          }}人</el-descriptions-item>
          <el-descriptions-item label="审批状态" label-class-name="nowrap-label">
          <el-descriptions-item label="审批状态"
                                label-class-name="nowrap-label">
            <el-tag :type="getStatusType(currentMeeting.status)">
              {{ getStatusText(currentMeeting.status) }}
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{
          <el-descriptions-item label="申请时间"
                                label-class-name="nowrap-label">{{
            currentMeeting.createTime
          }}</el-descriptions-item>
          <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2"
            label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item>
          <el-descriptions-item style="max-height: 400px"
                                label="会议说明"
                                :span="2"
                                label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item>
        </el-descriptions>
        <div class="content-section mt-20">
          <h4>参会人员</h4>
          <div class="participants-list">
            <el-tag
              v-for="participant in currentMeeting.participants"
              :key="participant.id"
              style="margin-right: 10px; margin-bottom: 10px;"
            >
            <el-tag v-for="participant in currentMeeting.participants"
                    :key="participant.id"
                    style="margin-right: 10px; margin-bottom: 10px;">
              {{ participant.name }}
            </el-tag>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="detailDialogVisible = false">关 闭</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 添加会议纪要对话框 -->
    <el-dialog
      title="添加会议纪要"
      v-model="minutesDialogVisible"
      width="80%"
      @close="handleCloseMinutesDialog"
    >
    <el-dialog title="添加会议纪要"
               v-model="minutesDialogVisible"
               width="80%"
               @close="handleCloseMinutesDialog">
      <div v-if="currentMeeting">
        <el-descriptions :column="2" border>
        <el-descriptions :column="2"
                         border>
          <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item>
          <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item>
          <el-descriptions-item label="主持人">{{ currentMeeting.host }}</el-descriptions-item>
          <el-descriptions-item label="会议时间" :span="2">
          <el-descriptions-item label="会议时间"
                                :span="2">
            {{ formatDateTime(currentMeeting.meetingTime) }}
          </el-descriptions-item>
          <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item>
          <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item>
        </el-descriptions>
        <div class="content-section mt-20">
          <h4>会议纪要内容</h4>
          <div class="editor-container">
            <Editor
              v-model="minutesContent"
              :min-height="400"
            />
            <Editor v-model="minutesContent"
                    :min-height="400" />
          </div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitMinutes">保 存</el-button>
          <el-button @click="minutesDialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="submitMinutes">保 存</el-button>
        </div>
      </template>
    </el-dialog>
@@ -162,242 +190,250 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import Pagination from '@/components/Pagination/index.vue'
import Editor from '@/components/Editor/index.vue'
import { getRoomEnum, getMeetingPublish ,getMeetingMinutesByMeetingId,saveMeetingMinutes} from '@/api/collaborativeApproval/meeting.js'
import { getStaffOnJob } from "@/api/personnelManagement/onboarding.js"
import dayjs from "dayjs"
  import { ref, reactive, onMounted } from "vue";
  import { ElMessage } from "element-plus";
  import Pagination from "@/components/Pagination/index.vue";
  import Editor from "@/components/Editor/index.vue";
  import {
    getRoomEnum,
    getMeetingPublish,
    getMeetingMinutesByMeetingId,
    saveMeetingMinutes,
  } from "@/api/collaborativeApproval/meeting.js";
  import { getStaffOnJob } from "@/api/personnelManagement/onboarding.js";
  import dayjs from "dayjs";
// 数据列表加载状态
const loading = ref(false)
  // 数据列表加载状态
  const loading = ref(false);
// 总条数
const total = ref(0)
const roomEnum = ref([])
const staffList = ref([])
  // 总条数
  const total = ref(0);
  const roomEnum = ref([]);
  const staffList = ref([]);
// 会议列表数据
const meetingList = ref([])
  // 会议列表数据
  const meetingList = ref([]);
// 查询参数
const queryParams = reactive({
  current: 1,
  size: 10
})
  // 查询参数
  const queryParams = reactive({
    current: 1,
    size: 10,
  });
// 搜索表单
const searchForm = reactive({
  title: '',
  applicant: '',
  // status: '1' // 默认只显示已通过审批的会议
})
  // 搜索表单
  const searchForm = reactive({
    title: "",
    applicant: "",
    // status: '1' // 默认只显示已通过审批的会议
  });
// 是否显示对话框
const detailDialogVisible = ref(false)
const minutesDialogVisible = ref(false)
  // 是否显示对话框
  const detailDialogVisible = ref(false);
  const minutesDialogVisible = ref(false);
// 当前查看的会议
const currentMeeting = ref(null)
  // 当前查看的会议
  const currentMeeting = ref(null);
// 会议纪要内容
const minutesContent = ref('')
const minutesContentId = ref('')
  // 会议纪要内容
  const minutesContent = ref("");
  const minutesContentId = ref("");
// 查询数据
const getList = async () => {
  loading.value = true
  let resp = await getMeetingPublish({ ...searchForm, ...queryParams })
  meetingList.value = resp.data.records.map(it => {
    let room = roomEnum.value.find(room => it.roomId === room.id)
    it.location = `${room.name}(${room.location})`
    let staffs = JSON.parse(it.participants)
    it.staffCount = staffs.size
    it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}`
    it.participants = staffList.value.filter(staff => staffs.some(id => id === staff.id)).map(staff => {
      return {
        id: staff.id,
        name: `${staff.staffName}(${staff.postJob})`
      }
    })
  // 查询数据
  const getList = async () => {
    loading.value = true;
    let resp = await getMeetingPublish({ ...searchForm, ...queryParams });
    meetingList.value = resp.data.records.map(it => {
      let room = roomEnum.value.find(room => it.roomId === room.id);
      it.location = `${room.name}(${room.location})`;
      let staffs = JSON.parse(it.participants);
      it.staffCount = staffs.size;
      it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format(
        "HH:mm:ss"
      )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`;
      it.participants = staffList.value
        .filter(staff => staffs.some(id => id === staff.id))
        .map(staff => {
          return {
            id: staff.id,
            name: `${staff.staffName}(${staff.postJob})`,
          };
        });
    return it
  })
  total.value = resp.data.total
  loading.value = false
}
      return it;
    });
    total.value = resp.data.total;
    loading.value = false;
  };
// 搜索按钮操作
const handleSearch = () => {
  queryParams.current = 1
  getList()
}
  // 搜索按钮操作
  const handleSearch = () => {
    queryParams.current = 1;
    getList();
  };
// 重置搜索表单
const resetSearch = () => {
  Object.assign(searchForm, {
    title: '',
    applicant: '',
    // status: '1'
  })
  handleSearch()
}
  // 重置搜索表单
  const resetSearch = () => {
    Object.assign(searchForm, {
      title: "",
      applicant: "",
      // status: '1'
    });
    handleSearch();
  };
// 查看详情
const viewDetail = (row) => {
  currentMeeting.value = row
  detailDialogVisible.value = true
}
  // 查看详情
  const viewDetail = row => {
    currentMeeting.value = row;
    detailDialogVisible.value = true;
  };
// 添加会议纪要
const addMinutes = async (row) => {
  let resp = await getMeetingMinutesByMeetingId(row.id)
  currentMeeting.value = row
  if (resp.data){
    minutesContent.value = resp.data.content
    minutesContentId.value = resp.data.id
  }else {
    minutesContent.value = `<h2>${row.title}会议纪要</h2>
<p><strong>会议时间:</strong>${row.meetingTime}</p>
<p><strong>会议地点:</strong>${row.location}</p>
<p><strong>主持人:</strong>${row.host}</p>
<p><strong>参会人员:</strong></p>
<ol>
  ${row.participants.map(p => `<li>${p.name}</li>`).join('')}
</ol>
<p><strong>会议内容:</strong></p>
<ol>
  <li>议题一:
    <ul>
      <li>讨论内容:</li>
      <li>决议事项:</li>
    </ul>
  </li>
  <li>议题二:
    <ul>
      <li>讨论内容:</li>
      <li>决议事项:</li>
    </ul>
  </li>
</ol>
<p><strong>备注:</strong></p>`
  }
  // 添加会议纪要
  const addMinutes = async row => {
    let resp = await getMeetingMinutesByMeetingId(row.id);
    currentMeeting.value = row;
    if (resp.data) {
      minutesContent.value = resp.data.content;
      minutesContentId.value = resp.data.id;
    } else {
      minutesContent.value = `<h2>${row.title}会议纪要</h2>
  <p><strong>会议时间:</strong>${row.meetingTime}</p>
  <p><strong>会议地点:</strong>${row.location}</p>
  <p><strong>主持人:</strong>${row.host}</p>
  <p><strong>参会人员:</strong></p>
  <ol>
    ${row.participants.map(p => `<li>${p.name}</li>`).join("")}
  </ol>
  <p><strong>会议内容:</strong></p>
  <ol>
    <li>议题一:
      <ul>
        <li>讨论内容:</li>
        <li>决议事项:</li>
      </ul>
    </li>
    <li>议题二:
      <ul>
        <li>讨论内容:</li>
        <li>决议事项:</li>
      </ul>
    </li>
  </ol>
  <p><strong>备注:</strong></p>`;
    }
  minutesDialogVisible.value = true
}
    minutesDialogVisible.value = true;
  };
// 提交会议纪要
const submitMinutes = () => {
  if (!minutesContent.value) {
    ElMessage.warning('请输入会议纪要内容')
    return
  }
  saveMeetingMinutes({
    id: minutesContentId.value,
    content: minutesContent.value,
    meetingId: currentMeeting.value.id,
    title: currentMeeting.value.title
  }).then(resp=>{
    console.log('会议纪要内容:', minutesContent.value)
    ElMessage.success('会议纪要保存成功')
    minutesDialogVisible.value = false
  })
  // 提交会议纪要
  const submitMinutes = () => {
    if (!minutesContent.value) {
      ElMessage.warning("请输入会议纪要内容");
      return;
    }
    saveMeetingMinutes({
      id: minutesContentId.value,
      content: minutesContent.value,
      meetingId: currentMeeting.value.id,
      title: currentMeeting.value.title,
    }).then(resp => {
      console.log("会议纪要内容:", minutesContent.value);
      ElMessage.success("会议纪要保存成功");
      minutesDialogVisible.value = false;
    });
  };
}
  // 关闭会议纪要对话框
  const handleCloseMinutesDialog = () => {
    minutesContent.value = "";
  };
// 关闭会议纪要对话框
const handleCloseMinutesDialog = () => {
  minutesContent.value = ''
}
  // 获取状态类型
  const getStatusType = status => {
    const statusMap = {
      0: "info", // 待审批
      1: "success", // 已通过
      2: "warning", // 未通过
      3: "danger", // 取消
    };
    return statusMap[status] || "info";
  };
// 获取状态类型
const getStatusType = (status) => {
  const statusMap = {
    '0': 'info',     // 待审批
    '1': 'success',  // 已通过
    '2': 'warning',  // 未通过
    '3': 'danger'   // 取消
  }
  return statusMap[status] || 'info'
}
  // 获取状态文本
  const getStatusText = status => {
    const statusMap = {
      0: "待审批",
      1: "已通过",
      2: "未通过",
      3: "已取消",
    };
    return statusMap[status] || "未知";
  };
// 获取状态文本
const getStatusText = (status) => {
  const statusMap = {
    '0': '待审批',
    '1': '已通过',
    '2': '未通过',
    '3': '已取消'
  }
  return statusMap[status] || '未知'
}
  // 格式化日期时间
  const formatDateTime = dateTime => {
    if (!dateTime) return "";
    return dateTime.replace(" ", "\n");
  };
// 格式化日期时间
const formatDateTime = (dateTime) => {
  if (!dateTime) return ''
  return dateTime.replace(' ', '\n')
}
  // 页面加载时获取数据
  onMounted(async () => {
    const [resp1, resp2] = await Promise.all([getRoomEnum(), getStaffOnJob()]);
    roomEnum.value = resp1.data;
    staffList.value = resp2.data;
// 页面加载时获取数据
onMounted(async () => {
  const [resp1, resp2] = await Promise.all([getRoomEnum(), getStaffOnJob()])
  roomEnum.value = resp1.data
  staffList.value = resp2.data
  await getList()
})
    await getList();
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
}
  .app-container {
    padding: 20px;
  }
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}
  .page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
  }
.page-header h2 {
  margin: 0;
  color: #303133;
}
  .page-header h2 {
    margin: 0;
    color: #303133;
  }
.search-card {
  margin-bottom: 20px;
}
  .search-card {
    margin-bottom: 20px;
  }
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
  }
.content-section h4 {
  margin: 0 0 15px 0;
  color: #303133;
}
  .content-section h4 {
    margin: 0 0 15px 0;
    color: #303133;
  }
.mt-20 {
  margin-top: 20px;
}
  .mt-20 {
    margin-top: 20px;
  }
.participants-list {
  min-height: 40px;
  padding: 15px;
  border-radius: 4px;
  line-height: 1.6;
}
  .participants-list {
    min-height: 40px;
    padding: 15px;
    border-radius: 4px;
    line-height: 1.6;
  }
.nowrap-label {
  white-space: nowrap !important;
}
  .nowrap-label {
    white-space: nowrap !important;
  }
.editor-container {
  border: 1px solid #dcdfe6;
  border-radius: 4px;
}
  .editor-container {
    border: 1px solid #dcdfe6;
    border-radius: 4px;
  }
</style>
src/views/collaborativeApproval/officeSupplies/index.vue
@@ -4,175 +4,241 @@
      <template #header>
        <div class="card-header">
          <span>办公物资申请管理</span>
          <el-button type="primary" @click="openShow()">
            <el-icon><Plus /></el-icon>
          <el-button type="primary"
                     @click="openShow()">
            <el-icon>
              <Plus />
            </el-icon>
            新建申请
          </el-button>
        </div>
      </template>
             <!-- 搜索区域 -->
       <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
         <el-form-item label="申请编号" prop="code">
           <el-input
             v-model="queryParams.code"
             placeholder="请输入申请编号"
             clearable
             style="width: 200px"
             @keyup.enter="handleQuery"
           />
         </el-form-item>
         <el-form-item label="申请人" prop="applicant">
           <el-input
             v-model="queryParams.applicant"
             placeholder="请输入申请人"
             clearable
             style="width: 200px"
             @keyup.enter="handleQuery"
           />
         </el-form-item>
         <el-form-item label="申请状态" prop="status">
           <el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 200px">
             <el-option label="待审批" value="1" />
             <el-option label="已通过" value="3" />
             <el-option label="已拒绝" value="2" />
             <el-option label="已发放" value="4" />
           </el-select>
         </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="handleQuery">
             <el-icon><Search /></el-icon>
             搜索
           </el-button>
           <el-button @click="resetQuery">
             <el-icon><Refresh /></el-icon>
             重置
           </el-button>
         </el-form-item>
         <el-form-item>
            <el-button type="primary" @click="handleExport">
            <el-icon><Download /></el-icon>
      <!-- 搜索区域 -->
      <el-form :model="queryParams"
               ref="queryRef"
               :inline="true"
               v-show="showSearch">
        <el-form-item label="申请编号"
                      prop="code">
          <el-input v-model="queryParams.code"
                    placeholder="请输入申请编号"
                    clearable
                    style="width: 200px"
                    @keyup.enter="handleQuery" />
        </el-form-item>
        <el-form-item label="申请人"
                      prop="applicant">
          <el-input v-model="queryParams.applicant"
                    placeholder="请输入申请人"
                    clearable
                    style="width: 200px"
                    @keyup.enter="handleQuery" />
        </el-form-item>
        <el-form-item label="申请状态"
                      prop="status">
          <el-select v-model="queryParams.status"
                     placeholder="请选择状态"
                     clearable
                     style="width: 200px">
            <el-option label="待审批"
                       value="1" />
            <el-option label="已通过"
                       value="3" />
            <el-option label="已拒绝"
                       value="2" />
            <el-option label="已发放"
                       value="4" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">
            <el-icon>
              <Search />
            </el-icon>
            搜索
          </el-button>
          <el-button @click="resetQuery">
            <el-icon>
              <Refresh />
            </el-icon>
            重置
          </el-button>
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleExport">
            <el-icon>
              <Download />
            </el-icon>
            导出
          </el-button>
         </el-form-item>
       </el-form>
        </el-form-item>
      </el-form>
      <!-- 表格区域 -->
      <el-table
        v-loading="loading"
        :data="officeList"
        @selection-change="handleSelectionChange"
        style="width: 100%"
      >
        <el-table-column type="selection" width="55" align="center" />
        <el-table-column label="申请编号" align="center" prop="code" width="180" />
        <el-table-column label="申请人" align="center" prop="applicant" width="120" />
        <el-table-column label="部门" align="center" prop="dept" width="120" />
        <el-table-column label="物资类型" align="center" prop="materialType" width="120">
      <el-table v-loading="loading"
                :data="officeList"
                @selection-change="handleSelectionChange"
                style="width: 100%">
        <el-table-column type="selection"
                         width="55"
                         align="center" />
        <el-table-column label="申请编号"
                         align="center"
                         prop="code"
                         width="180" />
        <el-table-column label="申请人"
                         align="center"
                         prop="applicant"
                         width="120" />
        <el-table-column label="部门"
                         align="center"
                         prop="dept"
                         width="120" />
        <el-table-column label="物资类型"
                         align="center"
                         prop="materialType"
                         width="120">
          <template #default="scope">
            <el-tag v-if="scope.row.materialType === 1" type="info">其他</el-tag>
            <el-tag v-if="scope.row.materialType === 2" type="success">清洁用品</el-tag>
            <el-tag v-if="scope.row.materialType === 3" type="warning">电子设备</el-tag>
            <el-tag v-if="scope.row.materialType === 4" type="danger">办公用品</el-tag>
            <el-tag v-if="scope.row.materialType === 1"
                    type="info">其他</el-tag>
            <el-tag v-if="scope.row.materialType === 2"
                    type="success">清洁用品</el-tag>
            <el-tag v-if="scope.row.materialType === 3"
                    type="warning">电子设备</el-tag>
            <el-tag v-if="scope.row.materialType === 4"
                    type="danger">办公用品</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="申请数量" align="center" prop="applyNum" width="100" />
        <el-table-column label="申请原因" align="center" prop="reason" min-width="200" show-overflow-tooltip />
        <el-table-column label="申请状态" align="center" prop="status" width="100">
        <el-table-column label="申请数量"
                         align="center"
                         prop="applyNum"
                         width="100" />
        <el-table-column label="申请原因"
                         align="center"
                         prop="reason"
                         min-width="200"
                         show-overflow-tooltip />
        <el-table-column label="申请状态"
                         align="center"
                         prop="status"
                         width="100">
          <template #default="scope">
            <el-tag :type="getStatusType(scope.row.status)">
              {{ getStatusText(scope.row.status) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="申请时间" align="center" prop="applyTime" width="180" />
        <el-table-column label="审批人" align="center" prop="approval" width="120" />
        <el-table-column label="审批时间" align="center" prop="approvalTime" width="180" />
        <el-table-column label="发放时间" align="center" prop="issueTime" width="180" />
        <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width" width="200">
        <el-table-column label="申请时间"
                         align="center"
                         prop="applyTime"
                         width="180" />
        <el-table-column label="审批人"
                         align="center"
                         prop="approval"
                         width="120" />
        <el-table-column label="审批时间"
                         align="center"
                         prop="approvalTime"
                         width="180" />
        <el-table-column label="发放时间"
                         align="center"
                         prop="issueTime"
                         width="180" />
        <el-table-column label="操作"
                         align="center"
                         fixed="right"
                         class-name="small-padding fixed-width"
                         width="200">
          <template #default="scope">
            <el-button
              v-if="scope.row.status === 1"
              type="primary"
              link
              @click="handleApprove(scope.row)"
            >
            <el-button v-if="scope.row.status === 1"
                       type="primary"
                       link
                       @click="handleApprove(scope.row)">
              审批
            </el-button>
            <el-button
              v-if="scope.row.status === 3"
              type="success"
                            link
              @click="handleIssue(scope.row)"
            >
            <el-button v-if="scope.row.status === 3"
                       type="success"
                       link
                       @click="handleIssue(scope.row)">
              发放
            </el-button>
            <el-button
              type="info"
                            link
              @click="handleDetail(scope.row)"
            >
            <el-button type="info"
                       link
                       @click="handleDetail(scope.row)">
              详情
            </el-button>
            <el-button
              v-if="scope.row.status === 2"
              type="danger"
                            link
              @click="handleDelete(scope.row)"
            >
            <el-button v-if="scope.row.status === 2"
                       type="danger"
                       link
                       @click="handleDelete(scope.row)">
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <pagination
        v-show="total > 0"
        :total="total"
        v-model:page="queryParams.current"
        v-model:limit="queryParams.size"
        @pagination="getList"
      />
      <pagination v-show="total > 0"
                  :total="total"
                  v-model:page="queryParams.current"
                  v-model:limit="queryParams.size"
                  @pagination="getList" />
    </el-card>
    <!-- 申请对话框 -->
    <el-dialog
      v-model="showApplyDialog"
      title="办公物资申请"
      width="600px"
      append-to-body
    >
      <el-form ref="applyFormRef" :model="applyForm" :rules="applyRules" label-width="100px">
        <el-form-item label="申请人" prop="applicant">
          <el-input v-model="applyForm.applicant" placeholder="请输入申请人名称" />
    <el-dialog v-model="showApplyDialog"
               title="办公物资申请"
               width="600px"
               append-to-body>
      <el-form ref="applyFormRef"
               :model="applyForm"
               :rules="applyRules"
               label-width="100px">
        <el-form-item label="申请人"
                      prop="applicant">
          <el-input v-model="applyForm.applicant"
                    placeholder="请输入申请人名称" />
        </el-form-item>
        <el-form-item label="部门" prop="dept">
          <el-input v-model="applyForm.dept" placeholder="请输入部门名称" />
        <el-form-item label="部门"
                      prop="dept">
          <el-input v-model="applyForm.dept"
                    placeholder="请输入部门名称" />
        </el-form-item>
        <el-form-item label="物资类型" prop="materialType">
          <el-select v-model="applyForm.materialType" placeholder="请选择物资类型" style="width: 100%">
            <el-option label="办公用品" value="4" />
            <el-option label="电子设备" value="3" />
            <el-option label="清洁用品" value="2" />
            <el-option label="其他" value="1" />
        <el-form-item label="物资类型"
                      prop="materialType">
          <el-select v-model="applyForm.materialType"
                     placeholder="请选择物资类型"
                     style="width: 100%">
            <el-option label="办公用品"
                       value="4" />
            <el-option label="电子设备"
                       value="3" />
            <el-option label="清洁用品"
                       value="2" />
            <el-option label="其他"
                       value="1" />
          </el-select>
        </el-form-item>
        <el-form-item label="具体物品" prop="itemName">
          <el-input v-model="applyForm.itemName" placeholder="请输入具体物品名称" />
        <el-form-item label="具体物品"
                      prop="itemName">
          <el-input v-model="applyForm.itemName"
                    placeholder="请输入具体物品名称" />
        </el-form-item>
        <el-form-item label="申请数量" prop="applyNum">
          <el-input-number v-model="applyForm.applyNum" :min="1" :max="999" style="width: 100%" />
        <el-form-item label="申请数量"
                      prop="applyNum">
          <el-input-number v-model="applyForm.applyNum"
                           :min="1"
                           :max="999"
                           style="width: 100%" />
        </el-form-item>
        <el-form-item label="申请原因" prop="reason">
          <el-input
            v-model="applyForm.reason"
            type="textarea"
            :rows="3"
            placeholder="请输入申请原因"
          />
        <el-form-item label="申请原因"
                      prop="reason">
          <el-input v-model="applyForm.reason"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入申请原因" />
        </el-form-item>
        <el-form-item label="紧急程度" prop="urgency">
        <el-form-item label="紧急程度"
                      prop="urgency">
          <el-radio-group v-model="applyForm.urgency">
            <el-radio label="1">普通</el-radio>
            <el-radio label="2">紧急</el-radio>
@@ -182,58 +248,59 @@
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitApply">确 定</el-button>
          <el-button @click="showApplyDialog = false">取 消</el-button>
          <el-button type="primary" @click="submitApply">确 定</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 审批对话框 -->
    <el-dialog
      v-model="showApproveDialog"
      title="审批申请"
      width="500px"
      append-to-body
    >
      <el-form ref="approveFormRef" :model="approveForm" :rules="approveRules" label-width="100px">
        <el-form-item label="审批结果" prop="approveResult">
    <el-dialog v-model="showApproveDialog"
               title="审批申请"
               width="500px"
               append-to-body>
      <el-form ref="approveFormRef"
               :model="approveForm"
               :rules="approveRules"
               label-width="100px">
        <el-form-item label="审批结果"
                      prop="approveResult">
          <el-radio-group v-model="approveForm.approveResult">
            <el-radio label="3">通过</el-radio>
            <el-radio label="2">拒绝</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="审批意见" prop="approvalOpinions">
          <el-input
            v-model="approveForm.approvalOpinions"
            type="textarea"
            :rows="3"
            placeholder="请输入审批意见"
          />
        <el-form-item label="审批意见"
                      prop="approvalOpinions">
          <el-input v-model="approveForm.approvalOpinions"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入审批意见" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitApprove">确 定</el-button>
          <el-button @click="showApproveDialog = false">取 消</el-button>
          <el-button type="primary" @click="submitApprove">确 定</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 详情对话框 -->
    <el-dialog
      v-model="showDetailDialog"
      title="申请详情"
      width="700px"
      append-to-body
    >
      <el-descriptions :column="2" border>
    <el-dialog v-model="showDetailDialog"
               title="申请详情"
               width="700px"
               append-to-body>
      <el-descriptions :column="2"
                       border>
        <el-descriptions-item label="申请编号">{{ currentDetail.code }}</el-descriptions-item>
        <el-descriptions-item label="申请人">{{ currentDetail.applicant }}</el-descriptions-item>
        <el-descriptions-item label="部门">{{ currentDetail.dept }}</el-descriptions-item>
        <el-descriptions-item label="物资类型">{{ currentDetail.materialType }}</el-descriptions-item>
        <el-descriptions-item label="具体物品">{{ currentDetail.itemName }}</el-descriptions-item>
        <el-descriptions-item label="申请数量">{{ currentDetail.applyNum }}</el-descriptions-item>
        <el-descriptions-item label="申请原因" :span="2">{{ currentDetail.reason }}</el-descriptions-item>
        <el-descriptions-item label="申请原因"
                              :span="2">{{ currentDetail.reason }}</el-descriptions-item>
        <el-descriptions-item label="申请状态">
          <el-tag :type="getStatusType(currentDetail.status)">
            {{ getStatusText(currentDetail.status) }}
@@ -242,7 +309,8 @@
        <el-descriptions-item label="申请时间">{{ currentDetail.applyTime }}</el-descriptions-item>
        <el-descriptions-item label="审批人">{{ currentDetail.approval || '-' }}</el-descriptions-item>
        <el-descriptions-item label="审批时间">{{ currentDetail.approvalTime || '-' }}</el-descriptions-item>
        <el-descriptions-item label="审批意见" :span="2">{{ currentDetail.approvalOpinions || '-' }}</el-descriptions-item>
        <el-descriptions-item label="审批意见"
                              :span="2">{{ currentDetail.approvalOpinions || '-' }}</el-descriptions-item>
        <el-descriptions-item label="发放时间">{{ currentDetail.issueTime || '-' }}</el-descriptions-item>
        <el-descriptions-item label="发放人">{{ currentDetail.issueUser || '-' }}</el-descriptions-item>
      </el-descriptions>
@@ -251,262 +319,277 @@
</template>
<script setup>
import {listPage,add,update,deleteOff} from "@/api/collaborativeApproval/officeSupplies.js"
import {ref, reactive, onMounted, getCurrentInstance} from 'vue'
import Cookies from 'js-cookie'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Search, Refresh, Download, Check } from '@element-plus/icons-vue'
  import {
    listPage,
    add,
    update,
    deleteOff,
  } from "@/api/collaborativeApproval/officeSupplies.js";
  import { ref, reactive, onMounted, getCurrentInstance } from "vue";
  import Cookies from "js-cookie";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    Plus,
    Search,
    Refresh,
    Download,
    Check,
  } from "@element-plus/icons-vue";
// 响应式数据
const loading = ref(false)
const showSearch = ref(true)
const showApplyDialog = ref(false)
const showApproveDialog = ref(false)
const showDetailDialog = ref(false)
const multipleSelection = ref([])
const officeList = ref([])
const total = ref(0)
const suppliesList = ref([])
const currentDetail = ref({})
  // 响应式数据
  const loading = ref(false);
  const showSearch = ref(true);
  const showApplyDialog = ref(false);
  const showApproveDialog = ref(false);
  const showDetailDialog = ref(false);
  const multipleSelection = ref([]);
  const officeList = ref([]);
  const total = ref(0);
  const suppliesList = ref([]);
  const currentDetail = ref({});
// 查询参数
const queryParams = reactive({
  current: 1,
  size: 10,
  code: '',
  applicant: '',
  status: ''
})
  // 查询参数
  const queryParams = reactive({
    current: 1,
    size: 10,
    code: "",
    applicant: "",
    status: "",
  });
// 申请表单
const applyForm = reactive({
  applicant: '',
  dept: '',
  materialType: '',
  itemName: '',
  applyNum: 1,
  reason: '',
  urgency: '1'
})
// 审批表单
const approveForm = reactive({
  approveResult: '3',
  approvalOpinions: ''
})
// 表单校验规则
const applyRules = {
  applicant: [{ required: true, message: '请选择物资类型', trigger: 'blur' }],
  dept: [{ required: true, message: '请选择物资类型', trigger: 'blur' }],
  materialType: [{ required: true, message: '请选择物资类型', trigger: 'change' }],
  itemName: [{ required: true, message: '请输入具体物品名称', trigger: 'blur' }],
  applyNum: [{ required: true, message: '请输入申请数量', trigger: 'blur' }],
  reason: [{ required: true, message: '请输入申请原因', trigger: 'blur' }]
}
const approveRules = {
  approveResult: [{ required: true, message: '请选择审批结果', trigger: 'change' }],
  approvalOpinions: [{ required: true, message: '请输入审批意见', trigger: 'blur' }]
}
const openShow = () => {
  showApplyDialog.value = true
  resetApplyForm()
}
// 获取列表数据
const getList = () => {
  loading.value = true
  listPage(queryParams).then(res => {
    total.value = res.data.total
    loading.value = false
    officeList.value = res.data.records
  })
}
// 查询
const handleQuery = () => {
  queryParams.current = 1
  getList()
}
// 重置查询
const resetQuery = () => {
  queryParams.code = ''
  queryParams.applicant = ''
  queryParams.status = ''
  handleQuery()
}
// 多选
const handleSelectionChange = (selection) => {
  multipleSelection.value = selection
}
// 获取状态类型
const getStatusType = (status) => {
  const statusMap = {
    1: 'warning',
    3: 'success',
    2: 'danger',
    4: 'info'
  }
  return statusMap[status] || 'info'
}
// 获取状态文本
const getStatusText = (status) => {
  const statusMap = {
    1: '待审批',
    3: '已通过',
    2: '已拒绝',
    4: '已发放'
  }
  return statusMap[status] || status
}
// 提交申请
const submitApply = () => {
  add(applyForm).then(() => {
    ElMessage.success('申请成功')
    getList()
    showApplyDialog.value = false
    resetApplyForm()
  })
}
//重置表单
const resetApplyForm = () => {
  // 重置表单
  Object.assign(applyForm, {
    applicant: '',
    dept: '',
    materialType: '',
    itemName: '',
  // 申请表单
  const applyForm = reactive({
    applicant: "",
    dept: "",
    materialType: "",
    itemName: "",
    applyNum: 1,
    reason: '',
    urgency: '1'
  })
}
    reason: "",
    urgency: "1",
  });
// 审批
const handleApprove = (row) => {
  currentDetail.value = row
  showApproveDialog.value = true
}
  // 审批表单
  const approveForm = reactive({
    approveResult: "3",
    approvalOpinions: "",
  });
const formatDate = (date) => {
  const year = date.getFullYear()
  const month = String(date.getMonth() + 1).padStart(2, '0')
  const day = String(date.getDate()).padStart(2, '0')
  const hours = String(date.getHours()).padStart(2, '0')
  const minutes = String(date.getMinutes()).padStart(2, '0')
  const sends = String(date.getSeconds()).padStart(2, '0')
  return `${year}-${month}-${day} ${hours}:${minutes}:${sends}`
}
  // 表单校验规则
  const applyRules = {
    applicant: [{ required: true, message: "请选择物资类型", trigger: "blur" }],
    dept: [{ required: true, message: "请选择物资类型", trigger: "blur" }],
    materialType: [
      { required: true, message: "请选择物资类型", trigger: "change" },
    ],
    itemName: [
      { required: true, message: "请输入具体物品名称", trigger: "blur" },
    ],
    applyNum: [{ required: true, message: "请输入申请数量", trigger: "blur" }],
    reason: [{ required: true, message: "请输入申请原因", trigger: "blur" }],
  };
// 提交审批
const submitApprove = () => {
  currentDetail.value.status = approveForm.approveResult
  // 从cookie中获取当前登录用户名称
  currentDetail.value.approval = Cookies.get('username')
  currentDetail.value.approvalTime = formatDate(new Date())
  currentDetail.value.approvalOpinions = approveForm.approvalOpinions
  update(currentDetail.value).then((res) => {
    if(res.code === 200){
      showApproveDialog.value = false
      ElMessage.success('审批完成')
      getList()
  const approveRules = {
    approveResult: [
      { required: true, message: "请选择审批结果", trigger: "change" },
    ],
    approvalOpinions: [
      { required: true, message: "请输入审批意见", trigger: "blur" },
    ],
  };
      // 重置表单
      Object.assign(approveForm, {
        approveResult: '3',
        approvalOpinions: ''
      })
    }
  })
  const openShow = () => {
    showApplyDialog.value = true;
    resetApplyForm();
  };
}
  // 获取列表数据
  const getList = () => {
    loading.value = true;
    listPage(queryParams).then(res => {
      total.value = res.data.total;
      loading.value = false;
      officeList.value = res.data.records;
    });
  };
// 发放
const handleIssue = (row) => {
  row.status = 4
  row.issueTime = formatDate(new Date())
  row.issueUser = Cookies.get('username')
  update(row).then((res) =>{
    if(res.code === 200){
      ElMessage.success('发放完成')
      getList()
    }
  })
}
  // 查询
  const handleQuery = () => {
    queryParams.current = 1;
    getList();
  };
// 查看详情
const handleDetail = (row) => {
  currentDetail.value = row
  showDetailDialog.value = true
}
  // 重置查询
  const resetQuery = () => {
    queryParams.code = "";
    queryParams.applicant = "";
    queryParams.status = "";
    handleQuery();
  };
// 删除
const handleDelete = (row) => {
  ElMessageBox.confirm('确认删除该申请吗?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    let ids = [row.id]
    deleteOff(ids).then((res) =>{
      ElMessage.success('删除成功')
      getList()
  // 多选
  const handleSelectionChange = selection => {
    multipleSelection.value = selection;
  };
  // 获取状态类型
  const getStatusType = status => {
    const statusMap = {
      1: "warning",
      3: "success",
      2: "danger",
      4: "info",
    };
    return statusMap[status] || "info";
  };
  // 获取状态文本
  const getStatusText = status => {
    const statusMap = {
      1: "待审批",
      3: "已通过",
      2: "已拒绝",
      4: "已发放",
    };
    return statusMap[status] || status;
  };
  // 提交申请
  const submitApply = () => {
    add(applyForm).then(() => {
      ElMessage.success("申请成功");
      getList();
      showApplyDialog.value = false;
      resetApplyForm();
    });
  };
  //重置表单
  const resetApplyForm = () => {
    // 重置表单
    Object.assign(applyForm, {
      applicant: "",
      dept: "",
      materialType: "",
      itemName: "",
      applyNum: 1,
      reason: "",
      urgency: "1",
    });
  };
  // 审批
  const handleApprove = row => {
    currentDetail.value = row;
    showApproveDialog.value = true;
  };
  const formatDate = date => {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const sends = String(date.getSeconds()).padStart(2, "0");
    return `${year}-${month}-${day} ${hours}:${minutes}:${sends}`;
  };
  // 提交审批
  const submitApprove = () => {
    currentDetail.value.status = approveForm.approveResult;
    // 从cookie中获取当前登录用户名称
    currentDetail.value.approval = Cookies.get("username");
    currentDetail.value.approvalTime = formatDate(new Date());
    currentDetail.value.approvalOpinions = approveForm.approvalOpinions;
    update(currentDetail.value).then(res => {
      if (res.code === 200) {
        showApproveDialog.value = false;
        ElMessage.success("审批完成");
        getList();
        // 重置表单
        Object.assign(approveForm, {
          approveResult: "3",
          approvalOpinions: "",
        });
      }
    });
  };
  // 发放
  const handleIssue = row => {
    row.status = 4;
    row.issueTime = formatDate(new Date());
    row.issueUser = Cookies.get("username");
    update(row).then(res => {
      if (res.code === 200) {
        ElMessage.success("发放完成");
        getList();
      }
    });
  };
  // 查看详情
  const handleDetail = row => {
    currentDetail.value = row;
    showDetailDialog.value = true;
  };
  // 删除
  const handleDelete = row => {
    ElMessageBox.confirm("确认删除该申请吗?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      let ids = [row.id];
      deleteOff(ids).then(res => {
        ElMessage.success("删除成功");
        getList();
      });
    });
  };
  const { proxy } = getCurrentInstance();
  // 导出
  const handleExport = () => {
    ElMessageBox.confirm("所有的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
  })
}
const { proxy } = getCurrentInstance();
// 导出
const handleExport = () => {
  ElMessageBox.confirm("所有的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/officeSupplies/export", {}, "办公物资.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
}
  };
// 页面加载时获取数据
onMounted(() => {
  getList()
})
  // 页面加载时获取数据
  onMounted(() => {
    getList();
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
}
  .app-container {
    padding: 20px;
  }
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
.mb8 {
  margin-bottom: 8px;
}
  .mb8 {
    margin-bottom: 8px;
  }
.dialog-footer {
  text-align: right;
}
  .dialog-footer {
    text-align: right;
  }
:deep(.el-descriptions__label) {
  width: 120px;
}
  :deep(.el-descriptions__label) {
    width: 120px;
  }
</style>
src/views/collaborativeApproval/planTemplate/index.vue
@@ -3,34 +3,45 @@
    <!-- 顶部操作栏 -->
    <div class="header-actions">
      <div class="left-actions">
        <el-select v-model="currentLevel" placeholder="选择计划级别" style="width: 150px" @change="handleLevelChange">
          <el-option label="个人计划" value="personal" />
          <el-option label="小组计划" value="group" />
          <el-option label="部门计划" value="department" />
          <el-option label="公司计划" value="company" />
        <el-select v-model="currentLevel"
                   placeholder="选择计划级别"
                   style="width: 150px"
                   @change="handleLevelChange">
          <el-option label="个人计划"
                     value="personal" />
          <el-option label="小组计划"
                     value="group" />
          <el-option label="部门计划"
                     value="department" />
          <el-option label="公司计划"
                     value="company" />
        </el-select>
        <el-select v-model="currentPeriod" placeholder="选择时间周期" style="width: 120px; margin-left: 10px" @change="handlePeriodChange">
          <el-option label="周计划" value="week" />
          <el-option label="月计划" value="month" />
          <el-option label="年计划" value="year" />
        <el-select v-model="currentPeriod"
                   placeholder="选择时间周期"
                   style="width: 120px; margin-left: 10px"
                   @change="handlePeriodChange">
          <el-option label="周计划"
                     value="week" />
          <el-option label="月计划"
                     value="month" />
          <el-option label="年计划"
                     value="year" />
        </el-select>
        <el-date-picker
          v-model="currentDate"
          :type="datePickerType"
          placeholder="选择日期"
          format="YYYY-MM-DD"
          value-format="YYYY-MM-DD"
          style="width: 180px; margin-left: 10px"
          @change="handleDateChange"
        />
        <el-date-picker v-model="currentDate"
                        :type="datePickerType"
                        placeholder="选择日期"
                        format="YYYY-MM-DD"
                        value-format="YYYY-MM-DD"
                        style="width: 180px; margin-left: 10px"
                        @change="handleDateChange" />
      </div>
      <div class="right-actions">
        <el-button type="primary" @click="handleAddPlan">新增计划</el-button>
        <el-button type="primary"
                   @click="handleAddPlan">新增计划</el-button>
        <el-button @click="handleExport">导出计划</el-button>
        <!-- <el-button @click="handleShare">共享计划@</el-button> -->
      </div>
    </div>
    <!-- 计划概览卡片 -->
    <div class="overview-cards">
      <el-row :gutter="20">
@@ -38,13 +49,16 @@
          <el-card class="overview-card">
            <div class="card-content">
              <div class="card-icon personal">
                <el-icon><User /></el-icon>
                <el-icon>
                  <User />
                </el-icon>
              </div>
              <div class="card-info">
                <div class="card-title">个人计划</div>
                <div class="card-number">{{ overviewData.personal.total }}</div>
                <div class="card-progress">
                  <el-progress :percentage="overviewData.personal.completion" :stroke-width="6" />
                  <el-progress :percentage="overviewData.personal.completion"
                               :stroke-width="6" />
                </div>
              </div>
            </div>
@@ -54,13 +68,16 @@
          <el-card class="overview-card">
            <div class="card-content">
              <div class="card-icon group">
                <el-icon><UserFilled /></el-icon>
                <el-icon>
                  <UserFilled />
                </el-icon>
              </div>
              <div class="card-info">
                <div class="card-title">小组计划</div>
                <div class="card-number">{{ overviewData.group.total }}</div>
                <div class="card-progress">
                  <el-progress :percentage="overviewData.group.completion" :stroke-width="6" />
                  <el-progress :percentage="overviewData.group.completion"
                               :stroke-width="6" />
                </div>
              </div>
            </div>
@@ -70,13 +87,16 @@
          <el-card class="overview-card">
            <div class="card-content">
              <div class="card-icon department">
                <el-icon><OfficeBuilding /></el-icon>
                <el-icon>
                  <OfficeBuilding />
                </el-icon>
              </div>
              <div class="card-info">
                <div class="card-title">部门计划</div>
                <div class="card-number">{{ overviewData.department.total }}</div>
                <div class="card-progress">
                  <el-progress :percentage="overviewData.department.completion" :stroke-width="6" />
                  <el-progress :percentage="overviewData.department.completion"
                               :stroke-width="6" />
                </div>
              </div>
            </div>
@@ -86,13 +106,16 @@
          <el-card class="overview-card">
            <div class="card-content">
              <div class="card-icon company">
                <el-icon><House /></el-icon>
                <el-icon>
                  <House />
                </el-icon>
              </div>
              <div class="card-info">
                <div class="card-title">公司计划</div>
                <div class="card-number">{{ overviewData.company.total }}</div>
                <div class="card-progress">
                  <el-progress :percentage="overviewData.company.completion" :stroke-width="6" />
                  <el-progress :percentage="overviewData.company.completion"
                               :stroke-width="6" />
                </div>
              </div>
            </div>
@@ -100,7 +123,6 @@
        </el-col>
      </el-row>
    </div>
    <!-- 计划列表 -->
    <div class="plan-content">
      <el-card>
@@ -108,68 +130,82 @@
          <div class="card-header">
            <span>{{ getCurrentLevelText() }} - {{ getCurrentPeriodText() }}</span>
            <div>
              <el-button size="small" @click="handleRefresh">刷新</el-button>
              <el-button size="small"
                         @click="handleRefresh">刷新</el-button>
              <!-- <el-button size="small" @click="handleFilter">筛选@</el-button> -->
            </div>
          </div>
        </template>
        <div class="plan-list">
          <div v-for="plan in planList" :key="plan.id" class="plan-item">
          <div v-for="plan in planList"
               :key="plan.id"
               class="plan-item">
            <div class="plan-header">
              <div class="plan-title">
                <el-tag :type="getPriorityType(plan.priority)" size="small">{{ getPriorityText(plan.priority) }}</el-tag>
                <el-tag :type="getPriorityType(plan.priority)"
                        size="small">{{ getPriorityText(plan.priority) }}</el-tag>
                <span class="title-text">{{ plan.title }}</span>
              </div>
              <div class="plan-actions">
                <el-button size="small" @click="handleEditPlan(plan)">编辑</el-button>
                <el-button size="small" @click="handleViewDetail(plan)">详情</el-button>
                <el-button size="small"
                           @click="handleEditPlan(plan)">编辑</el-button>
                <el-button size="small"
                           @click="handleViewDetail(plan)">详情</el-button>
                <el-dropdown @command="(command) => handleMoreAction(plan, command)">
                  <el-button size="small">
                    更多<el-icon class="el-icon--right"><ArrowDown /></el-icon>
                    更多<el-icon class="el-icon--right">
                      <ArrowDown />
                    </el-icon>
                  </el-button>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <!-- <el-dropdown-item command="share">共享@</el-dropdown-item> -->
                      <el-dropdown-item command="copy">复制</el-dropdown-item>
                      <el-dropdown-item command="delete" divided>删除</el-dropdown-item>
                      <el-dropdown-item command="delete"
                                        divided>删除</el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </div>
            </div>
            <div class="plan-content">
              <div class="plan-description">{{ plan.description }}</div>
              <div class="plan-meta">
                <div class="meta-item">
                  <el-icon><Calendar /></el-icon>
                  <el-icon>
                    <Calendar />
                  </el-icon>
                  <span>{{ plan.startDate }} - {{ plan.endDate }}</span>
                </div>
                <div class="meta-item">
                  <el-icon><User /></el-icon>
                  <el-icon>
                    <User />
                  </el-icon>
                  <span>{{ plan.assignee }}</span>
                </div>
                <div class="meta-item">
                  <el-icon><Clock /></el-icon>
                  <el-icon>
                    <Clock />
                  </el-icon>
                  <span>进度: {{ plan.progress }}%</span>
                </div>
                <div class="meta-item">
                  <el-icon><Flag /></el-icon>
                  <el-icon>
                    <Flag />
                  </el-icon>
                  <span>{{ getStatusText(plan.status) }}</span>
                </div>
              </div>
              <div class="plan-progress">
                <el-progress
                  :percentage="plan.progress"
                  :color="getProgressColor(plan.progress)"
                  :stroke-width="8"
                />
                <el-progress :percentage="plan.progress"
                             :color="getProgressColor(plan.progress)"
                             :stroke-width="8" />
              </div>
              <div class="plan-tags">
                <el-tag v-for="tag in plan.tags" :key="tag" size="small" style="margin-right: 5px">
                <el-tag v-for="tag in plan.tags"
                        :key="tag"
                        size="small"
                        style="margin-right: 5px">
                  {{ tag }}
                </el-tag>
              </div>
@@ -178,75 +214,96 @@
        </div>
      </el-card>
    </div>
    <!-- 新增/编辑计划对话框 -->
    <el-dialog
      v-model="planDialogVisible"
      :title="operationType === 'add' ? '发布计划' : '编辑计划'"
      width="600px"
      @close="handleDialogClose"
    >
      <el-form :model="planForm" :rules="planRules" ref="planFormRef" label-width="100px">
        <el-form-item label="计划标题" prop="title">
          <el-input v-model="planForm.title" placeholder="请输入计划标题" />
    <el-dialog v-model="planDialogVisible"
               :title="operationType === 'add' ? '发布计划' : '编辑计划'"
               width="600px"
               @close="handleDialogClose">
      <el-form :model="planForm"
               :rules="planRules"
               ref="planFormRef"
               label-width="100px">
        <el-form-item label="计划标题"
                      prop="title">
          <el-input v-model="planForm.title"
                    placeholder="请输入计划标题" />
        </el-form-item>
        <el-form-item label="计划描述" prop="description">
          <el-input
            v-model="planForm.description"
            type="textarea"
            :rows="3"
            placeholder="请输入计划描述"
          />
        <el-form-item label="计划描述"
                      prop="description">
          <el-input v-model="planForm.description"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入计划描述" />
        </el-form-item>
        <el-form-item label="计划级别" prop="level">
          <el-select v-model="planForm.level" placeholder="选择计划级别" style="width: 100%">
            <el-option label="个人计划" value="personal" />
            <el-option label="小组计划" value="group" />
            <el-option label="部门计划" value="department" />
            <el-option label="公司计划" value="company" />
        <el-form-item label="计划级别"
                      prop="level">
          <el-select v-model="planForm.level"
                     placeholder="选择计划级别"
                     style="width: 100%">
            <el-option label="个人计划"
                       value="personal" />
            <el-option label="小组计划"
                       value="group" />
            <el-option label="部门计划"
                       value="department" />
            <el-option label="公司计划"
                       value="company" />
          </el-select>
        </el-form-item>
        <el-form-item label="时间周期" prop="period">
          <el-select v-model="planForm.period" placeholder="选择时间周期" style="width: 100%">
            <el-option label="周计划" value="week" />
            <el-option label="月计划" value="month" />
            <el-option label="年计划" value="year" />
        <el-form-item label="时间周期"
                      prop="period">
          <el-select v-model="planForm.period"
                     placeholder="选择时间周期"
                     style="width: 100%">
            <el-option label="周计划"
                       value="week" />
            <el-option label="月计划"
                       value="month" />
            <el-option label="年计划"
                       value="year" />
          </el-select>
        </el-form-item>
        <el-form-item label="开始时间" prop="startDate">
          <el-date-picker
            v-model="planForm.startDate"
            type="date"
            value-format="YYYY-MM-DD"
            format="YYYY-MM-DD"
            placeholder="选择开始时间"
            style="width: 100%"
          />
        <el-form-item label="开始时间"
                      prop="startDate">
          <el-date-picker v-model="planForm.startDate"
                          type="date"
                          value-format="YYYY-MM-DD"
                          format="YYYY-MM-DD"
                          placeholder="选择开始时间"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="结束时间" prop="endDate">
          <el-date-picker
            v-model="planForm.endDate"
            type="date"
            value-format="YYYY-MM-DD"
            format="YYYY-MM-DD"
            placeholder="选择结束时间"
            style="width: 100%"
          />
        <el-form-item label="结束时间"
                      prop="endDate">
          <el-date-picker v-model="planForm.endDate"
                          type="date"
                          value-format="YYYY-MM-DD"
                          format="YYYY-MM-DD"
                          placeholder="选择结束时间"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="负责人" prop="assignee">
          <el-input v-model="planForm.assignee" placeholder="请输入负责人" />
        <el-form-item label="负责人"
                      prop="assignee">
          <el-input v-model="planForm.assignee"
                    placeholder="请输入负责人" />
        </el-form-item>
        <el-form-item label="优先级" prop="priority">
          <el-select v-model="planForm.priority" placeholder="选择优先级" style="width: 100%">
            <el-option label="高" value="high" />
            <el-option label="中" value="medium" />
            <el-option label="低" value="low" />
        <el-form-item label="优先级"
                      prop="priority">
          <el-select v-model="planForm.priority"
                     placeholder="选择优先级"
                     style="width: 100%">
            <el-option label="高"
                       value="high" />
            <el-option label="中"
                       value="medium" />
            <el-option label="低"
                       value="low" />
          </el-select>
        </el-form-item>
        <!-- <el-form-item label="标签">
          <el-input v-model="planForm.tags" placeholder="请输入标签,用逗号分隔" />
        </el-form-item> -->
        <el-form-item label="标签" prop="tags">
        <el-form-item label="标签"
                      prop="tags">
          <!-- <el-checkbox-group v-model="planForm.tags">
            <el-checkbox label="all"></el-checkbox>
            <el-checkbox label="manager">管理层</el-checkbox>
@@ -254,50 +311,57 @@
            <el-checkbox label="finance">财务部门</el-checkbox>
            <el-checkbox label="tech">技术部门</el-checkbox>
          </el-checkbox-group> -->
          <el-select
            v-model="planForm.tags"
            multiple
            placeholder="请选择标签"
            style="width: 100%"
          >
            <el-option
              v-for="dept in departments"
              :key="dept"
              :label="dept"
              :value="dept"
            />
          <el-select v-model="planForm.tags"
                     multiple
                     placeholder="请选择标签"
                     style="width: 100%">
            <el-option v-for="dept in departments"
                       :key="dept"
                       :label="dept"
                       :value="dept" />
          </el-select>
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-select v-model="planForm.status" placeholder="选择状态" style="width: 100%">
            <el-option label="未开始" value="not_started" />
            <el-option label="进行中" value="in_progress" />
            <el-option label="已完成" value="completed" />
            <el-option label="已暂停" value="paused" />
        <el-form-item label="状态"
                      prop="status">
          <el-select v-model="planForm.status"
                     placeholder="选择状态"
                     style="width: 100%">
            <el-option label="未开始"
                       value="not_started" />
            <el-option label="进行中"
                       value="in_progress" />
            <el-option label="已完成"
                       value="completed" />
            <el-option label="已暂停"
                       value="paused" />
          </el-select>
        </el-form-item>
        <el-form-item label="进度" prop="progress">
          <el-input-number
            v-model="planForm.progress"
            min="0"
            max="100"
            step="1"
            placeholder="请输入进度"
            style="width: 100%"
          />
        <el-form-item label="进度"
                      prop="progress">
          <el-input-number v-model="planForm.progress"
                           min="0"
                           max="100"
                           step="1"
                           placeholder="请输入进度"
                           style="width: 100%" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="handleSavePlan">保存</el-button>
          <el-button @click="planDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="handleSavePlan">保存</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 计划详情对话框 -->
    <el-dialog v-model="showPlanDetailDialog" title="计划详情" width="700px">
      <div v-if="currentPlanDetail" class="mb10">
        <el-descriptions :column="2" border>
    <el-dialog v-model="showPlanDetailDialog"
               title="计划详情"
               width="700px">
      <div v-if="currentPlanDetail"
           class="mb10">
        <el-descriptions :column="2"
                         border>
          <el-descriptions-item label="计划标题">{{ currentPlanDetail.title }}</el-descriptions-item>
          <el-descriptions-item label="计划描述">{{ currentPlanDetail.description }}</el-descriptions-item>
          <el-descriptions-item label="计划级别">{{ getCurrentLevelText(currentPlanDetail.level) }}</el-descriptions-item>
@@ -316,552 +380,573 @@
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
const { proxy } = getCurrentInstance();
import {
  User,
  UserFilled,
  OfficeBuilding,
  House,
  Calendar,
  Clock,
  Flag,
  ArrowDown
} from '@element-plus/icons-vue'
import { listDutyPlan, addDutyPlan, updateDutyPlan, delDutyPlan,NumDutyPlan,exportDutyPlan } from '@/api/collaborativeApproval/planTemplate.js'
  import { ref, reactive, computed, onMounted } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  const { proxy } = getCurrentInstance();
  import {
    User,
    UserFilled,
    OfficeBuilding,
    House,
    Calendar,
    Clock,
    Flag,
    ArrowDown,
  } from "@element-plus/icons-vue";
  import {
    listDutyPlan,
    addDutyPlan,
    updateDutyPlan,
    delDutyPlan,
    NumDutyPlan,
    exportDutyPlan,
  } from "@/api/collaborativeApproval/planTemplate.js";
// 响应式数据
const operationType = ref('add')
const currentLevel = ref('personal')
const currentPeriod = ref('week')
const currentDate = ref(new Date())
const planDialogVisible = ref(false)
const dialogTitle = ref('新增计划')
const planFormRef = ref()
const showPlanDetailDialog = ref(false)
const currentPlanDetail = ref(null)
  // 响应式数据
  const operationType = ref("add");
  const currentLevel = ref("personal");
  const currentPeriod = ref("week");
  const currentDate = ref(new Date());
  const planDialogVisible = ref(false);
  const dialogTitle = ref("新增计划");
  const planFormRef = ref();
  const showPlanDetailDialog = ref(false);
  const currentPlanDetail = ref(null);
// 表单数据
const planForm = reactive({
  id: '',
  title: '',
  description: '',
  level: 'personal',
  period: 'week',
  startDate: '',
  endDate: '',
  assignee: '',
  priority: 'medium',
  tags: [],
  status: '',
  progress: 0
})
  // 表单数据
  const planForm = reactive({
    id: "",
    title: "",
    description: "",
    level: "personal",
    period: "week",
    startDate: "",
    endDate: "",
    assignee: "",
    priority: "medium",
    tags: [],
    status: "",
    progress: 0,
  });
// 表单验证规则
const planRules = {
  title: [{ required: true, message: '请输入计划标题', trigger: 'blur' }],
  description: [{ required: true, message: '请输入计划描述', trigger: 'blur' }],
  level: [{ required: true, message: '请选择计划级别', trigger: 'change' }],
  period: [{ required: true, message: '请选择时间周期', trigger: 'change' }],
  startDate: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
  endDate: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
  assignee: [{ required: true, message: '请输入负责人', trigger: 'blur' }],
  priority: [{ required: true, message: '请选择优先级', trigger: 'change' }]
}
const departments = ["产品", "分析", "调研",'技术', '架构', '设计','市场', '推广', '营销'];
// 概览数据
const overviewData = reactive({
  personal: { total: 0, completion: 0 },
  group: { total: 0, completion: 0 },
  department: { total: 0, completion: 0 },
  company: { total: 0, completion: 0 }
})
  // 表单验证规则
  const planRules = {
    title: [{ required: true, message: "请输入计划标题", trigger: "blur" }],
    description: [{ required: true, message: "请输入计划描述", trigger: "blur" }],
    level: [{ required: true, message: "请选择计划级别", trigger: "change" }],
    period: [{ required: true, message: "请选择时间周期", trigger: "change" }],
    startDate: [{ required: true, message: "请选择开始时间", trigger: "change" }],
    endDate: [{ required: true, message: "请选择结束时间", trigger: "change" }],
    assignee: [{ required: true, message: "请输入负责人", trigger: "blur" }],
    priority: [{ required: true, message: "请选择优先级", trigger: "change" }],
  };
  const departments = [
    "产品",
    "分析",
    "调研",
    "技术",
    "架构",
    "设计",
    "市场",
    "推广",
    "营销",
  ];
  // 概览数据
  const overviewData = reactive({
    personal: { total: 0, completion: 0 },
    group: { total: 0, completion: 0 },
    department: { total: 0, completion: 0 },
    company: { total: 0, completion: 0 },
  });
// 计划列表数据
const planList = ref([])
  // 计划列表数据
  const planList = ref([]);
// 计算属性
const datePickerType = computed(() => {
  switch (currentPeriod.value) {
    case 'week':
      return 'week'
    case 'month':
      return 'month'
    case 'year':
      return 'year'
    default:
      return 'date'
  }
})
  // 计算属性
  const datePickerType = computed(() => {
    switch (currentPeriod.value) {
      case "week":
        return "week";
      case "month":
        return "month";
      case "year":
        return "year";
      default:
        return "date";
    }
  });
// 方法
const handleLevelChange = (value) => {
  console.log('计划级别变更:', value)
  getPlanList()
  // 这里可以根据级别筛选数据
}
  // 方法
  const handleLevelChange = value => {
    console.log("计划级别变更:", value);
    getPlanList();
    // 这里可以根据级别筛选数据
  };
const handlePeriodChange = (value) => {
  console.log('时间周期变更:', value)
  getPlanList()
  // 这里可以根据周期筛选数据
}
  const handlePeriodChange = value => {
    console.log("时间周期变更:", value);
    getPlanList();
    // 这里可以根据周期筛选数据
  };
const handleDateChange = (value) => {
  console.log('日期变更:', value)
  getPlanList()
  // 这里可以根据日期筛选数据
}
  const handleDateChange = value => {
    console.log("日期变更:", value);
    getPlanList();
    // 这里可以根据日期筛选数据
  };
const handleAddPlan = () => {
  operationType.value = 'add'
  dialogTitle.value = '新增计划'
  planDialogVisible.value = true
  // 重置表单
  Object.keys(planForm).forEach(key => {
    planForm[key] = ''
  })
  planForm.level = 'personal'
  planForm.period = 'week'
  planForm.priority = 'medium'
  planForm.status = 'not_started'
  planForm.progress = 0
}
  const handleAddPlan = () => {
    operationType.value = "add";
    dialogTitle.value = "新增计划";
    planDialogVisible.value = true;
    // 重置表单
    Object.keys(planForm).forEach(key => {
      planForm[key] = "";
    });
    planForm.level = "personal";
    planForm.period = "week";
    planForm.priority = "medium";
    planForm.status = "not_started";
    planForm.progress = 0;
  };
const handleEditPlan = (plan) => {
  operationType.value = 'edit'
  dialogTitle.value = '编辑计划'
  planDialogVisible.value = true
  Object.assign(planForm, plan)
  // // 填充表单数据
  // Object.keys(planForm).forEach(key => {
  //   if (key === 'tags') {
  //     planForm[key] = plan[key].join(', ')
  //   } else {
  //     planForm[key] = plan[key]
  //   }
  // })
}
  const handleEditPlan = plan => {
    operationType.value = "edit";
    dialogTitle.value = "编辑计划";
    planDialogVisible.value = true;
    Object.assign(planForm, plan);
    // // 填充表单数据
    // Object.keys(planForm).forEach(key => {
    //   if (key === 'tags') {
    //     planForm[key] = plan[key].join(', ')
    //   } else {
    //     planForm[key] = plan[key]
    //   }
    // })
  };
const handleViewDetail = (plan) => {
  currentPlanDetail.value = plan
  showPlanDetailDialog.value = true
  // ElMessage.info(`查看计划详情: ${plan.title}`)
}
  const handleViewDetail = plan => {
    currentPlanDetail.value = plan;
    showPlanDetailDialog.value = true;
    // ElMessage.info(`查看计划详情: ${plan.title}`)
  };
const handleMoreAction = async(plan,command) => {
  let ids = [];
  ids.push(plan.id);
  console.log("ids",ids)
  switch (command) {
    case 'share':
      ElMessage.success('计划已共享')
      break
    case 'copy':
      const knowledgeText = `
        计划标题:${plan.title}
        计划描述:${plan.description}
        计划级别:${getCurrentLevelText(plan.level)}
        时间周期:${getCurrentPeriodText(plan.period)}
        开始时间:${plan.startDate}
        结束时间:${plan.endDate}
        负责人:${plan.assignee}
        优先级:${getPriorityText(plan.priority)}
        标签:${plan.tags.join(', ')}
        状态:${getStatusText(plan.status)}
        进度:${plan.progress}%
      `.trim();
  const handleMoreAction = async (plan, command) => {
    let ids = [];
    ids.push(plan.id);
    console.log("ids", ids);
    switch (command) {
      case "share":
        ElMessage.success("计划已共享");
        break;
      case "copy":
        const knowledgeText = `
          计划标题:${plan.title}
          计划描述:${plan.description}
          计划级别:${getCurrentLevelText(plan.level)}
          时间周期:${getCurrentPeriodText(plan.period)}
          开始时间:${plan.startDate}
          结束时间:${plan.endDate}
          负责人:${plan.assignee}
          优先级:${getPriorityText(plan.priority)}
          标签:${plan.tags.join(", ")}
          状态:${getStatusText(plan.status)}
          进度:${plan.progress}%
        `.trim();
        // 复制到剪贴板
        navigator.clipboard.writeText(knowledgeText).then(() => {
          ElMessage.success("知识内容已复制到剪贴板");
        }).catch(() => {
          ElMessage.error("复制失败,请手动复制");
        navigator.clipboard
          .writeText(knowledgeText)
          .then(() => {
            ElMessage.success("知识内容已复制到剪贴板");
          })
          .catch(() => {
            ElMessage.error("复制失败,请手动复制");
          });
        // ElMessage.success('计划已复制')
        break;
      case "delete":
        ElMessageBox.confirm("确定要删除这个计划吗?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }).then(() => {
          delDutyPlan(ids).then(res => {
            if (res.code === 200) {
              ElMessage.success("计划已删除");
              ids.value = [];
              getPlanList();
            }
          });
        });
      // ElMessage.success('计划已复制')
      break
    case 'delete':
      ElMessageBox.confirm('确定要删除这个计划吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        delDutyPlan(ids).then(res => {
        break;
    }
  };
  //
  const handleSavePlan = async () => {
    try {
      await planFormRef.value.validate();
      if (operationType.value === "add") {
        addDutyPlan(planForm).then(res => {
          if (res.code === 200) {
            ElMessage.success('计划已删除')
            ids.value = [];
            getPlanList()
            ElMessage.success("计划保存成功");
            planDialogVisible.value = false;
          }
        })
      })
      break
  }
}
//
const handleSavePlan = async () => {
  try {
    await planFormRef.value.validate()
    if (operationType.value === 'add') {
      addDutyPlan(planForm).then(res => {
        if (res.code === 200) {
          ElMessage.success('计划保存成功')
          planDialogVisible.value = false
        }
        getPlanList()
      })
    } else {
      updateDutyPlan(planForm).then(res => {
        if (res.code === 200) {
          ElMessage.success('计划保存成功')
          planDialogVisible.value = false
        }
        getPlanList()
      })
          getPlanList();
        });
      } else {
        updateDutyPlan(planForm).then(res => {
          if (res.code === 200) {
            ElMessage.success("计划保存成功");
            planDialogVisible.value = false;
          }
          getPlanList();
        });
      }
    } catch (error) {
      console.log("表单验证失败:", error);
    }
  } catch (error) {
    console.log('表单验证失败:', error)
  }
}
  };
const handleDialogClose = () => {
  planFormRef.value?.resetFields()
}
  const handleDialogClose = () => {
    planFormRef.value?.resetFields();
  };
const handleRefresh = () => {
  getPlanList()
  // ElMessage.success('数据已刷新')
}
  const handleRefresh = () => {
    getPlanList();
    // ElMessage.success('数据已刷新')
  };
const handleFilter = () => {
  ElMessage.info('打开筛选面板')
}
  const handleFilter = () => {
    ElMessage.info("打开筛选面板");
  };
const handleExport = () => {
  ElMessageBox.confirm("是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      // exportDutyPlan().then(res => {
      // })
      proxy.download("/dutyPlan/export", {}, "计划管理.xlsx");
  const handleExport = () => {
    ElMessageBox.confirm("是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
const handleShare = () => {
  ElMessage.success('计划已共享')
}
      .then(() => {
        // exportDutyPlan().then(res => {
const getCurrentLevelText = () => {
  const levelMap = {
    personal: '个人计划',
    group: '小组计划',
    department: '部门计划',
    company: '公司计划'
  }
  return levelMap[currentLevel.value] || '个人计划'
}
const getCurrentPeriodText = () => {
  const periodMap = {
    week: '周计划',
    month: '月计划',
    year: '年计划'
  }
  return periodMap[currentPeriod.value] || '周计划'
}
const getPriorityType = (priority) => {
  const typeMap = {
    high: 'danger',
    medium: 'warning',
    low: 'info'
  }
  return typeMap[priority] || 'info'
}
const getPriorityText = (priority) => {
  const textMap = {
    high: '高',
    medium: '中',
    low: '低'
  }
  return textMap[priority] || '中'
}
const getStatusText = (status) => {
  const statusMap = {
    not_started: '未开始',
    in_progress: '进行中',
    completed: '已完成',
    paused: '已暂停'
  }
  return statusMap[status] || '未知'
}
const getProgressColor = (progress) => {
  if (progress >= 80) return '#67C23A'
  if (progress >= 50) return '#E6A23C'
  return '#F56C6C'
}
//获取数据列表
const getPlanList = async () => {
  const params = {
    level: currentLevel.value,
    period: currentPeriod.value,
    queryDate:currentDate.value
  }
  listDutyPlan(params).then(res => {
    if (res.code === 200) {
      planList.value = res.data.records
    }
  }).catch(err => {
    console.log(err)
  })
}
//获取数据
const getPlanNum = async () => {
  NumDutyPlan().then(res => {
    if (res.code === 200) {
      // console.log(res.data)
      //讲结果里面的数据根据level 赋值给overviewData
      res.data.forEach(item => {
        overviewData[item.level].total = item.num
        overviewData[item.level].completion = item.completion
        // })
        proxy.download("/dutyPlan/export", {}, "计划管理.xlsx");
      })
    }
  }).catch(err => {
    console.log(err)
  })
}
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  const handleShare = () => {
    ElMessage.success("计划已共享");
  };
onMounted(() => {
  getPlanList()
  getPlanNum()
  console.log('多级计划模板页面已加载')
})
  const getCurrentLevelText = () => {
    const levelMap = {
      personal: "个人计划",
      group: "小组计划",
      department: "部门计划",
      company: "公司计划",
    };
    return levelMap[currentLevel.value] || "个人计划";
  };
  const getCurrentPeriodText = () => {
    const periodMap = {
      week: "周计划",
      month: "月计划",
      year: "年计划",
    };
    return periodMap[currentPeriod.value] || "周计划";
  };
  const getPriorityType = priority => {
    const typeMap = {
      high: "danger",
      medium: "warning",
      low: "info",
    };
    return typeMap[priority] || "info";
  };
  const getPriorityText = priority => {
    const textMap = {
      high: "高",
      medium: "中",
      low: "低",
    };
    return textMap[priority] || "中";
  };
  const getStatusText = status => {
    const statusMap = {
      not_started: "未开始",
      in_progress: "进行中",
      completed: "已完成",
      paused: "已暂停",
    };
    return statusMap[status] || "未知";
  };
  const getProgressColor = progress => {
    if (progress >= 80) return "#67C23A";
    if (progress >= 50) return "#E6A23C";
    return "#F56C6C";
  };
  //获取数据列表
  const getPlanList = async () => {
    const params = {
      level: currentLevel.value,
      period: currentPeriod.value,
      queryDate: currentDate.value,
    };
    listDutyPlan(params)
      .then(res => {
        if (res.code === 200) {
          planList.value = res.data.records;
        }
      })
      .catch(err => {
        console.log(err);
      });
  };
  //获取数据
  const getPlanNum = async () => {
    NumDutyPlan()
      .then(res => {
        if (res.code === 200) {
          // console.log(res.data)
          //讲结果里面的数据根据level 赋值给overviewData
          res.data.forEach(item => {
            overviewData[item.level].total = item.num;
            overviewData[item.level].completion = item.completion;
          });
        }
      })
      .catch(err => {
        console.log(err);
      });
  };
  onMounted(() => {
    getPlanList();
    getPlanNum();
    console.log("多级计划模板页面已加载");
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
  background-color: #f5f5f5;
  min-height: 100vh;
}
  .app-container {
    padding: 20px;
    background-color: #f5f5f5;
    min-height: 100vh;
  }
.header-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  background: white;
  padding: 20px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.left-actions {
  display: flex;
  align-items: center;
}
.right-actions {
  display: flex;
  gap: 10px;
}
.overview-cards {
  margin-bottom: 20px;
}
.overview-card {
  height: 120px;
}
.card-content {
  display: flex;
  align-items: center;
  height: 100%;
}
.card-icon {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: 15px;
  font-size: 24px;
  color: white;
}
.card-icon.personal {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card-icon.group {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.card-icon.department {
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.card-icon.company {
  background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
.card-info {
  flex: 1;
}
.card-title {
  font-size: 14px;
  color: #666;
  margin-bottom: 5px;
}
.card-number {
  font-size: 24px;
  font-weight: bold;
  color: #333;
  margin-bottom: 10px;
}
.card-progress {
  width: 100%;
}
.plan-content {
  background: white;
  border-radius: 8px;
  overflow: hidden;
}
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: bold;
  color: #333;
}
.header-actions {
  display: flex;
  gap: 10px;
}
.plan-list {
  padding: 20px 0;
}
.plan-item {
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  margin-bottom: 15px;
  padding: 20px;
  transition: all 0.3s ease;
}
.plan-item:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  transform: translateY(-2px);
}
.plan-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}
.plan-title {
  display: flex;
  align-items: center;
  gap: 10px;
}
.title-text {
  font-size: 16px;
  font-weight: bold;
  color: #333;
}
.plan-actions {
  display: flex;
  gap: 10px;
}
.plan-content {
  margin-bottom: 15px;
}
.plan-description {
  color: #666;
  margin-bottom: 15px;
  line-height: 1.6;
}
.plan-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin-bottom: 15px;
}
.meta-item {
  display: flex;
  align-items: center;
  gap: 5px;
  color: #666;
  font-size: 14px;
}
.plan-progress {
  margin-bottom: 15px;
}
.plan-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
/* 响应式设计 */
@media (max-width: 768px) {
  .header-actions {
    flex-direction: column;
    gap: 15px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    background: white;
    padding: 20px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }
  .left-actions {
    flex-wrap: wrap;
    display: flex;
    align-items: center;
  }
  .right-actions {
    display: flex;
    gap: 10px;
  }
  .plan-meta {
    flex-direction: column;
  .overview-cards {
    margin-bottom: 20px;
  }
  .overview-card {
    height: 120px;
  }
  .card-content {
    display: flex;
    align-items: center;
    height: 100%;
  }
  .card-icon {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 15px;
    font-size: 24px;
    color: white;
  }
  .card-icon.personal {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  }
  .card-icon.group {
    background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  }
  .card-icon.department {
    background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
  }
  .card-icon.company {
    background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
  }
  .card-info {
    flex: 1;
  }
  .card-title {
    font-size: 14px;
    color: #666;
    margin-bottom: 5px;
  }
  .card-number {
    font-size: 24px;
    font-weight: bold;
    color: #333;
    margin-bottom: 10px;
  }
  .card-progress {
    width: 100%;
  }
  .plan-content {
    background: white;
    border-radius: 8px;
    overflow: hidden;
  }
  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-weight: bold;
    color: #333;
  }
  .header-actions {
    display: flex;
    gap: 10px;
  }
  .plan-list {
    padding: 20px 0;
  }
  .plan-item {
    border: 1px solid #e4e7ed;
    border-radius: 8px;
    margin-bottom: 15px;
    padding: 20px;
    transition: all 0.3s ease;
  }
  .plan-item:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    transform: translateY(-2px);
  }
  .plan-header {
    flex-direction: column;
    align-items: flex-start;
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
  }
  .plan-title {
    display: flex;
    align-items: center;
    gap: 10px;
  }
}
  .title-text {
    font-size: 16px;
    font-weight: bold;
    color: #333;
  }
  .plan-actions {
    display: flex;
    gap: 10px;
  }
  .plan-content {
    margin-bottom: 15px;
  }
  .plan-description {
    color: #666;
    margin-bottom: 15px;
    line-height: 1.6;
  }
  .plan-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-bottom: 15px;
  }
  .meta-item {
    display: flex;
    align-items: center;
    gap: 5px;
    color: #666;
    font-size: 14px;
  }
  .plan-progress {
    margin-bottom: 15px;
  }
  .plan-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
  }
  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
  }
  /* 响应式设计 */
  @media (max-width: 768px) {
    .header-actions {
      flex-direction: column;
      gap: 15px;
    }
    .left-actions {
      flex-wrap: wrap;
      gap: 10px;
    }
    .plan-meta {
      flex-direction: column;
      gap: 10px;
    }
    .plan-header {
      flex-direction: column;
      align-items: flex-start;
      gap: 10px;
    }
  }
</style>
src/views/collaborativeApproval/rpaManagement/index.vue
@@ -3,85 +3,93 @@
    <div class="search_form">
      <div>
        <span class="search_title">程序名:</span>
        <el-input
          v-model="searchForm.programName"
          style="width: 240px"
          placeholder="请输入程序名搜索"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
        <el-input v-model="searchForm.programName"
                  style="width: 240px"
                  placeholder="请输入程序名搜索"
                  @change="handleQuery"
                  clearable
                  :prefix-icon="Search" />
        <span class="search_title ml10">执行状态:</span>
        <el-select v-model="searchForm.status" clearable @change="handleQuery" style="width: 240px">
          <el-option label="运行中" :value="'running'" />
          <el-option label="已停止" :value="'stopped'" />
          <el-option label="异常" :value="'error'" />
        <el-select v-model="searchForm.status"
                   clearable
                   @change="handleQuery"
                   style="width: 240px">
          <el-option label="运行中"
                     :value="'running'" />
          <el-option label="已停止"
                     :value="'stopped'" />
          <el-option label="异常"
                     :value="'error'" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
        <el-button type="primary"
                   @click="handleQuery"
                   style="margin-left: 10px">
          搜索
        </el-button>
      </div>
      <div>
        <el-button @click="handleExport" style="margin-right: 10px">导出</el-button>
        <el-button type="primary" @click="openForm('add')">新增</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
        <el-button @click="handleExport"
                   style="margin-right: 10px">导出</el-button>
        <el-button type="primary"
                   @click="openForm('add')">新增</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
        :total="page.total"
      ></PIMTable>
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :isSelection="true"
                @selection-change="handleSelectionChange"
                :tableLoading="tableLoading"
                @pagination="pagination"
                :total="page.total"></PIMTable>
    </div>
    <!-- RPA表单弹窗 -->
    <el-dialog
      v-model="dialogVisible"
      :title="dialogTitle"
      width="500px"
      :close-on-click-modal="false"
    >
      <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
      >
        <el-form-item label="程序名" prop="programName">
          <el-input
            v-model="form.programName"
            placeholder="请输入程序名"
            clearable
          />
    <el-dialog v-model="dialogVisible"
               :title="dialogTitle"
               width="500px"
               :close-on-click-modal="false">
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="100px">
        <el-form-item label="程序名"
                      prop="programName">
          <el-input v-model="form.programName"
                    placeholder="请输入程序名"
                    clearable />
        </el-form-item>
        <el-form-item label="执行状态" prop="status">
          <el-select v-model="form.status" placeholder="请选择执行状态" style="width: 100%">
            <el-option label="运行中" value="running" />
            <el-option label="已停止" value="stopped" />
            <el-option label="异常" value="error" />
        <el-form-item label="执行状态"
                      prop="status">
          <el-select v-model="form.status"
                     placeholder="请选择执行状态"
                     style="width: 100%">
            <el-option label="运行中"
                       value="running" />
            <el-option label="已停止"
                       value="stopped" />
            <el-option label="异常"
                       value="error" />
          </el-select>
        </el-form-item>
        <el-form-item label="描述" prop="description">
          <el-input
            v-model="form.description"
            type="textarea"
            :rows="3"
            placeholder="请输入RPA程序描述"
            clearable
          />
        <el-form-item label="描述"
                      prop="description">
          <el-input v-model="form.description"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入RPA程序描述"
                    clearable />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="submitForm">确定</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="submitForm">确定</el-button>
        </span>
      </template>
    </el-dialog>
@@ -89,278 +97,304 @@
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import {listRpa, addRpa, updateRpa, delRpa, delRpaBatch} from "@/api/collaborativeApproval/rpaManagement.js";
// 响应式数据
const data = reactive({
  searchForm: {
    programName: "",
    status: "",
  },
  form: {
    programName: "",
    status: "",
    description: ""
  },
  dialogVisible: false,
  dialogTitle: "",
  dialogType: "add",
  tableLoading: false,
  page: {
    current: 1,
    size: 20,
    total: 0,
  },
  tableData: [],
});
const { searchForm, form, dialogVisible, dialogTitle, dialogType, selectedIds, tableLoading, page, tableData } = toRefs(data);
// 表单引用
const formRef = ref();
// 选择的行数据
const selectedRows = ref([]);
// 表单验证规则
const rules = {
  programName: [
    { required: true, message: "请输入程序名", trigger: "blur" }
  ],
  status: [
    { required: true, message: "请选择执行状态", trigger: "change" }
  ]
};
// 表格列配置
const tableColumn = ref([
  {
    label: "程序名",
    prop: "programName",
    // width: 200,
  },
  {
    label: "执行状态",
    prop: "status",
    dataType: "tag",
    // width: 120,
    formatData: (params) => {
      const statusMap = {
        running: "运行中",
        stopped: "已停止",
        error: "异常"
      };
      return statusMap[params] || params;
  import { Search } from "@element-plus/icons-vue";
  import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  import {
    listRpa,
    addRpa,
    updateRpa,
    delRpa,
    delRpaBatch,
  } from "@/api/collaborativeApproval/rpaManagement.js";
  // 响应式数据
  const data = reactive({
    searchForm: {
      programName: "",
      status: "",
    },
    formatType: (params) => {
      const typeMap = {
        running: "success",
        stopped: "info",
        error: "danger"
      };
      return typeMap[params] || "info";
    }
  },
  {
    label: "描述",
    prop: "description",
    // width: 300,
    showOverflowTooltip: true,
  },
  {
    label: "创建时间",
    prop: "createTime",
    // width: 180,
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 150,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        }
    form: {
      programName: "",
      status: "",
      description: "",
    },
    dialogVisible: false,
    dialogTitle: "",
    dialogType: "add",
    tableLoading: false,
    page: {
      current: 1,
      size: 20,
      total: 0,
    },
    tableData: [],
  });
  const {
    searchForm,
    form,
    dialogVisible,
    dialogTitle,
    dialogType,
    selectedIds,
    tableLoading,
    page,
    tableData,
  } = toRefs(data);
  // 表单引用
  const formRef = ref();
  // 选择的行数据
  const selectedRows = ref([]);
  // 表单验证规则
  const rules = {
    programName: [{ required: true, message: "请输入程序名", trigger: "blur" }],
    status: [{ required: true, message: "请选择执行状态", trigger: "change" }],
  };
  // 表格列配置
  const tableColumn = ref([
    {
      label: "程序名",
      prop: "programName",
      // width: 200,
    },
    {
      label: "执行状态",
      prop: "status",
      dataType: "tag",
      // width: 120,
      formatData: params => {
        const statusMap = {
          running: "运行中",
          stopped: "已停止",
          error: "异常",
        };
        return statusMap[params] || params;
      },
      // {
      //   name: "开始",
      //   type: "text",
      //   clickFun: (row) => {
      //     handleStart(row);
      //   },
      //   disabled: (row) => row.status !== 'stopped'
      // },
      // {
      //   name: "停止",
      //   type: "text",
      //   clickFun: (row) => {
      //     handleStop(row);
      //   },
      //   disabled: (row) => row.status === 'stopped'
      // }
    ]
  }
]);
// 生命周期
onMounted(() => {
  getList();
});
// 查询数据
const handleQuery = () => {
  // page.value.current = 1;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  listRpa({...page.value, ...searchForm.value})
  .then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
};
// 分页处理
const pagination = (obj) => {
  page.value.current = obj.page;
  page.value.size = obj.limit;
  handleQuery();
};
// 选择变化处理
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// 打开表单
const openForm = (type, row) => {
  dialogType.value = type;
  dialogVisible.value = true;
  if (type === "add") {
    dialogTitle.value = "添加RPA";
  } else {
    dialogTitle.value = "编辑RPA";
    form.value = { ...row };
  }
};
// 提交表单
const submitForm = async () => {
  if (!formRef.value) return;
  try {
    await formRef.value.validate();
    if (dialogType.value === "add") {
      // 添加新RPA
      addRpa({...form.value}).then(res => {
        if(res.code == 200){
          ElMessage.success("添加成功");
            form.value = {
            programName: "",
            status: "",
            description: ""
      formatType: params => {
        const typeMap = {
          running: "success",
          stopped: "info",
          error: "danger",
        };
        return typeMap[params] || "info";
      },
    },
    {
      label: "描述",
      prop: "description",
      // width: 300,
      showOverflowTooltip: true,
    },
    {
      label: "创建时间",
      prop: "createTime",
      // width: 180,
    },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: 150,
      operation: [
        {
          name: "编辑",
          type: "text",
          clickFun: row => {
            openForm("edit", row);
          },
          dialogVisible.value = false;
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
        },
        // {
        //   name: "开始",
        //   type: "text",
        //   clickFun: (row) => {
        //     handleStart(row);
        //   },
        //   disabled: (row) => row.status !== 'stopped'
        // },
        // {
        //   name: "停止",
        //   type: "text",
        //   clickFun: (row) => {
        //     handleStop(row);
        //   },
        //   disabled: (row) => row.status === 'stopped'
        // }
      ],
    },
  ]);
  // 生命周期
  onMounted(() => {
    getList();
  });
  // 查询数据
  const handleQuery = () => {
    // page.value.current = 1;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    listRpa({ ...page.value, ...searchForm.value })
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(err => {
        tableLoading.value = false;
      });
  };
  // 分页处理
  const pagination = obj => {
    page.value.current = obj.page;
    page.value.size = obj.limit;
    handleQuery();
  };
  // 选择变化处理
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  // 打开表单
  const openForm = (type, row) => {
    dialogType.value = type;
    dialogVisible.value = true;
    if (type === "add") {
      dialogTitle.value = "添加RPA";
    } else {
      // 编辑RPA
      updateRpa({...form.value}).then(res => {
        if(res.code == 200){
          ElMessage.success("更新成功");
          dialogVisible.value = false;
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
      dialogTitle.value = "编辑RPA";
      form.value = { ...row };
    }
  } catch (error) {
    console.error("表单验证失败:", error);
  }
};
  };
// 开始RPA
const handleStart = (row) => {
  ElMessageBox.confirm(`确定要启动RPA程序"${row.programName}"吗?`, "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    row.status = "running";
    ElMessage.success("RPA启动成功");
    getList();
  }).catch(() => {
    // 用户取消
  });
};
  // 提交表单
  const submitForm = async () => {
    if (!formRef.value) return;
// 停止RPA
const handleStop = (row) => {
  ElMessageBox.confirm(`确定要停止RPA程序"${row.programName}"吗?`, "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    row.status = "stopped";
    ElMessage.success("RPA停止成功");
    getList();
  }).catch(() => {
    // 用户取消
  });
};
    try {
      await formRef.value.validate();
// 删除RPA
const handleDelete = () => {
  let ids = [];
    if (selectedRows.value.length > 0) {
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
    .then(() => {
        delRpa(ids).then((res) => {
                if(res.code == 200){
                    ElMessage.success("删除成功");
                    getList();
                }
            }).catch(err => {
                ElMessage.error(err.msg);
            })
    })
    .catch(() => {
        proxy.$modal.msg("已取消");
    });
};
      if (dialogType.value === "add") {
        // 添加新RPA
        addRpa({ ...form.value })
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("添加成功");
              (form.value = {
                programName: "",
                status: "",
                description: "",
              }),
                (dialogVisible.value = false);
              getList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      } else {
        // 编辑RPA
        updateRpa({ ...form.value })
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("更新成功");
              dialogVisible.value = false;
              getList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      }
    } catch (error) {
      console.error("表单验证失败:", error);
    }
  };
// 导出功能
const { proxy } = getCurrentInstance()
const handleExport = () => {
  proxy.download('/rpaProcessAutomation/export', { ...searchForm.value }, 'RPA管理.xlsx')
}
  // 开始RPA
  const handleStart = row => {
    ElMessageBox.confirm(`确定要启动RPA程序"${row.programName}"吗?`, "提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        row.status = "running";
        ElMessage.success("RPA启动成功");
        getList();
      })
      .catch(() => {
        // 用户取消
      });
  };
  // 停止RPA
  const handleStop = row => {
    ElMessageBox.confirm(`确定要停止RPA程序"${row.programName}"吗?`, "提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        row.status = "stopped";
        ElMessage.success("RPA停止成功");
        getList();
      })
      .catch(() => {
        // 用户取消
      });
  };
  // 删除RPA
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delRpa(ids)
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("删除成功");
              getList();
            }
          })
          .catch(err => {
            ElMessage.error(err.msg);
          });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // 导出功能
  const { proxy } = getCurrentInstance();
  const handleExport = () => {
    proxy.download(
      "/rpaProcessAutomation/export",
      { ...searchForm.value },
      "RPA管理.xlsx"
    );
  };
</script>
<style scoped></style>
src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
@@ -1,70 +1,108 @@
<template>
  <div class="app-container">
        <!-- 规章制度管理-->
          <el-card class="box-card">
            <template #header>
              <div class="card-header">
                <span>规章制度发布</span>
              </div>
    <!-- 规章制度管理-->
    <el-card class="box-card">
      <template #header>
        <div class="card-header">
          <span>规章制度发布</span>
        </div>
      </template>
      <div class="tab-content">
        <el-row :gutter="20"
                class="mb-20">
          <span class="ml-10">制度标题:</span>
          <el-col :span="6">
            <el-input v-model="regulationSearchForm.title"
                      placeholder="请输入制度标题"
                      clearable />
          </el-col>
          <span class="search_title">制度分类:</span>
          <el-col :span="4">
            <el-select v-model="regulationSearchForm.category"
                       placeholder="制度分类"
                       clearable>
              <el-option label="人事制度"
                         value="hr" />
              <el-option label="财务制度"
                         value="finance" />
              <el-option label="安全制度"
                         value="safety" />
              <el-option label="技术制度"
                         value="tech" />
            </el-select>
          </el-col>
          <el-col :span="8">
            <el-button type="primary"
                       @click="searchRegulations">搜索</el-button>
            <el-button @click="resetRegulationSearch">重置</el-button>
            <el-button @click="handleExport">导出</el-button>
            <el-button type="success"
                       @click="handleAdd">
              发布制度
            </el-button>
          </el-col>
        </el-row>
        <el-table :data="regulations"
                  border
                  v-loading="tableLoading"
                  style="width: 100%">
          <el-table-column prop="regulationNum"
                           label="制度编号"
                           width="120" />
          <el-table-column prop="title"
                           label="制度标题"
                           min-width="150" />
          <el-table-column prop="category"
                           label="分类"
                           width="120">
            <template #default="scope">
              <el-tag>{{ getCategoryText(scope.row.category) }}</el-tag>
            </template>
            <div class="tab-content">
              <el-row :gutter="20" class="mb-20">
                <span class="ml-10">制度标题:</span>
                <el-col :span="6">
                  <el-input v-model="regulationSearchForm.title" placeholder="请输入制度标题" clearable />
                </el-col>
                <span class="search_title">制度分类:</span>
                <el-col :span="4">
                  <el-select v-model="regulationSearchForm.category" placeholder="制度分类" clearable>
                    <el-option label="人事制度" value="hr" />
                    <el-option label="财务制度" value="finance" />
                    <el-option label="安全制度" value="safety" />
                    <el-option label="技术制度" value="tech" />
                  </el-select>
                </el-col>
                <el-col :span="8">
                  <el-button type="primary" @click="searchRegulations">搜索</el-button>
                  <el-button @click="resetRegulationSearch">重置</el-button>
                  <el-button @click="handleExport">导出</el-button>
                  <el-button type="success" @click="handleAdd">
                    发布制度
                  </el-button>
                </el-col>
              </el-row>
              <el-table :data="regulations" border v-loading="tableLoading"  style="width: 100%">
                <el-table-column prop="regulationNum" label="制度编号" width="120" />
                <el-table-column prop="title" label="制度标题" min-width="150" />
                <el-table-column prop="category" label="分类" width="120">
                  <template #default="scope">
                    <el-tag>{{ getCategoryText(scope.row.category) }}</el-tag>
                  </template>
                </el-table-column>
                <el-table-column prop="version" label="版本" width="120" />
                <el-table-column prop="createUserName" label="发布人" width="120" />
                <el-table-column prop="createTime" label="发布时间" width="180" />
                <el-table-column prop="status" label="状态" width="100">
                  <template #default="scope">
                    <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
                      {{ scope.row.status === 'active' ? '生效中' : '已废止' }}
                    </el-tag>
                  </template>
                </el-table-column>
                <el-table-column prop="readCount" label="已读人数" width="100" />
                <el-table-column label="操作" width="250" fixed="right">
                  <template #default="scope">
                    <el-button link @click="viewRegulation(scope.row)">查看</el-button>
                    <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button>
                    <el-button link type="danger" @click="repealEdit(scope.row)">废弃</el-button>
                    <el-button link type="success" @click="viewVersionHistory(scope.row)">版本历史</el-button>
                    <el-button link type="warning" @click="viewReadStatus(scope.row)">阅读状态</el-button>
                  </template>
                </el-table-column>
              </el-table>
            </div>
          </el-card>
          </el-table-column>
          <el-table-column prop="version"
                           label="版本"
                           width="120" />
          <el-table-column prop="createUserName"
                           label="发布人"
                           width="120" />
          <el-table-column prop="createTime"
                           label="发布时间"
                           width="180" />
          <el-table-column prop="status"
                           label="状态"
                           width="100">
            <template #default="scope">
              <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
                {{ scope.row.status === 'active' ? '生效中' : '已废止' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="readCount"
                           label="已读人数"
                           width="100" />
          <el-table-column label="操作"
                           width="250"
                           fixed="right">
            <template #default="scope">
              <el-button link
                         @click="viewRegulation(scope.row)">查看</el-button>
              <el-button link
                         type="primary"
                         @click="handleEdit(scope.row)">编辑</el-button>
              <el-button link
                         type="danger"
                         @click="repealEdit(scope.row)">废弃</el-button>
              <el-button link
                         type="success"
                         @click="viewVersionHistory(scope.row)">版本历史</el-button>
              <el-button link
                         type="warning"
                         @click="viewReadStatus(scope.row)">阅读状态</el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </el-card>
    <!-- 用印申请对话框 -->
    <!-- <el-dialog v-model="showSealApplyDialog" title="申请用印" width="600px">
      <el-form :model="sealForm" :rules="sealRules" ref="sealFormRef" label-width="100px">
@@ -100,35 +138,62 @@
        </span>
      </template>
    </el-dialog> -->
    <!-- 规章制度发布对话框 -->
    <el-dialog v-model="showRegulationDialog" :title="operationType === 'add' ? '发布制度' : '编辑制度'" width="800px">
      <el-form :model="regulationForm" :rules="regulationRules" ref="regulationFormRef" label-width="100px">
        <el-form-item label="制度编号" prop="regulationNum">
          <el-input v-model="regulationForm.regulationNum" placeholder="请输入制度编号" />
    <el-dialog v-model="showRegulationDialog"
               :title="operationType === 'add' ? '发布制度' : '编辑制度'"
               width="800px">
      <el-form :model="regulationForm"
               :rules="regulationRules"
               ref="regulationFormRef"
               label-width="100px">
        <el-form-item label="制度编号"
                      prop="regulationNum">
          <el-input v-model="regulationForm.regulationNum"
                    placeholder="请输入制度编号" />
        </el-form-item>
        <el-form-item label="制度标题" prop="title">
          <el-input v-model="regulationForm.title" placeholder="请输入制度标题" />
        <el-form-item label="制度标题"
                      prop="title">
          <el-input v-model="regulationForm.title"
                    placeholder="请输入制度标题" />
        </el-form-item>
        <el-form-item label="制度分类" prop="category">
          <el-select v-model="regulationForm.category" placeholder="请选择制度分类" style="width: 100%">
            <el-option label="人事制度" value="hr" />
            <el-option label="财务制度" value="finance" />
            <el-option label="安全制度" value="safety" />
            <el-option label="技术制度" value="tech" />
        <el-form-item label="制度分类"
                      prop="category">
          <el-select v-model="regulationForm.category"
                     placeholder="请选择制度分类"
                     style="width: 100%">
            <el-option label="人事制度"
                       value="hr" />
            <el-option label="财务制度"
                       value="finance" />
            <el-option label="安全制度"
                       value="safety" />
            <el-option label="技术制度"
                       value="tech" />
          </el-select>
        </el-form-item>
        <el-form-item label="制度内容" prop="content">
          <el-input v-model="regulationForm.content" type="textarea" :rows="10" placeholder="请输入制度详细内容" />
        <el-form-item label="制度内容"
                      prop="content">
          <el-input v-model="regulationForm.content"
                    type="textarea"
                    :rows="10"
                    placeholder="请输入制度详细内容" />
        </el-form-item>
        <el-form-item label="制度版本" prop="version">
          <el-input v-model="regulationForm.version" placeholder="请输入制度版本" />
        <el-form-item label="制度版本"
                      prop="version">
          <el-input v-model="regulationForm.version"
                    placeholder="请输入制度版本" />
        </el-form-item>
        <el-form-item label="生效时间" prop="effectiveTime">
          <el-date-picker v-model="regulationForm.effectiveTime" type="datetime" format="YYYY-MM-DD HH:mm:ss"
             value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择生效时间" style="width: 100%" />
        <el-form-item label="生效时间"
                      prop="effectiveTime">
          <el-date-picker v-model="regulationForm.effectiveTime"
                          type="datetime"
                          format="YYYY-MM-DD HH:mm:ss"
                          value-format="YYYY-MM-DD HH:mm:ss"
                          placeholder="选择生效时间"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="适用范围" prop="scope">
        <el-form-item label="适用范围"
                      prop="scope">
          <el-checkbox-group v-model="regulationForm.scope">
            <el-checkbox label="all">全体员工</el-checkbox>
            <el-checkbox label="manager">管理层</el-checkbox>
@@ -137,19 +202,20 @@
            <el-checkbox label="tech">技术部门</el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="是否需要确认" prop="requireConfirm">
        <el-form-item label="是否需要确认"
                      prop="requireConfirm">
          <el-switch v-model="regulationForm.requireConfirm" />
          <span class="ml-10">开启后员工需要阅读确认</span>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="submitRegulation">发布制度</el-button>
          <el-button @click="showRegulationDialog = false">取消</el-button>
          <el-button type="primary" @click="submitRegulation">发布制度</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 用印详情对话框 -->
    <!-- <el-dialog v-model="showSealDetailDialog" title="用印申请详情" width="700px">
      <div v-if="currentSealDetail" class="mb10">
@@ -169,11 +235,13 @@
        </el-descriptions>
      </div>
    </el-dialog> -->
    <!-- 规章制度详情对话框 -->
    <el-dialog v-model="showRegulationDetailDialog" title="规章制度详情" width="800px">
    <el-dialog v-model="showRegulationDetailDialog"
               title="规章制度详情"
               width="800px">
      <div v-if="currentRegulationDetail">
        <el-descriptions :column="2" border>
        <el-descriptions :column="2"
                         border>
          <el-descriptions-item label="制度编号">{{ currentRegulationDetail.id }}</el-descriptions-item>
          <el-descriptions-item label="制度标题">{{ currentRegulationDetail.title }}</el-descriptions-item>
          <el-descriptions-item label="分类">{{ getCategoryText(currentRegulationDetail.category) }}</el-descriptions-item>
@@ -186,19 +254,30 @@
          <div class="regulation-content">{{ currentRegulationDetail.content }}</div>
        </div>
        <!-- 如果tableData>0 显示 -->
        <div style="margin: 10px 0;" v-if="tableData && tableData.length > 0" >
          <el-button type="success" @click="resetForm(currentRegulationDetail)">确认查看</el-button>
        <div style="margin: 10px 0;"
             v-if="tableData && tableData.length > 0">
          <el-button type="success"
                     @click="resetForm(currentRegulationDetail)">确认查看</el-button>
        </div>
      </div>
    </el-dialog>
    <!-- 版本历史对话框 -->
    <el-dialog v-model="showVersionHistoryDialog" title="版本历史" width="800px">
      <el-table :data="versionHistory" style="width: 100%;margin-bottom: 10px">
        <el-table-column prop="version" label="版本号" width="100" />
        <el-table-column prop="updateTime" label="更新时间" width="180" />
        <el-table-column prop="createUserName" label="更新人" width="120" />
        <el-table-column prop="changeLog" label="变更说明">
    <el-dialog v-model="showVersionHistoryDialog"
               title="版本历史"
               width="800px">
      <el-table :data="versionHistory"
                style="width: 100%;margin-bottom: 10px">
        <el-table-column prop="version"
                         label="版本号"
                         width="100" />
        <el-table-column prop="updateTime"
                         label="更新时间"
                         width="180" />
        <el-table-column prop="createUserName"
                         label="更新人"
                         width="120" />
        <el-table-column prop="changeLog"
                         label="变更说明">
          <template #default="scope">
            <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
              {{ scope.row.status === 'active' ? '生效中' : '已废止' }}
@@ -207,15 +286,27 @@
        </el-table-column>
      </el-table>
    </el-dialog>
    <!-- 阅读状态对话框 -->
    <el-dialog v-model="showReadStatusDialog" title="阅读状态" width="800px">
      <el-table :data="readStatusList" style="width: 100%;margin-bottom: 10px">
        <el-table-column prop="employee" label="员工姓名" width="120" />
        <el-table-column prop="department" label="所属部门" width="150" />
        <el-table-column prop="createTime" label="阅读时间" width="180" />
        <el-table-column prop="confirmTime" label="确认时间" width="180" />
        <el-table-column prop="status" label="状态" width="100">
    <el-dialog v-model="showReadStatusDialog"
               title="阅读状态"
               width="800px">
      <el-table :data="readStatusList"
                style="width: 100%;margin-bottom: 10px">
        <el-table-column prop="employee"
                         label="员工姓名"
                         width="120" />
        <el-table-column prop="department"
                         label="所属部门"
                         width="150" />
        <el-table-column prop="createTime"
                         label="阅读时间"
                         width="180" />
        <el-table-column prop="confirmTime"
                         label="确认时间"
                         width="180" />
        <el-table-column prop="status"
                         label="状态"
                         width="100">
          <template #default="scope">
            <el-tag :type="scope.row.status === 'confirmed' ? 'success' : 'warning'">
              {{ scope.row.status === 'confirmed' ? '已确认' : '未确认' }}
@@ -228,526 +319,564 @@
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import { listSealApplication, addSealApplication, updateSealApplication,listRuleManagement,addRuleManagement,updateRuleManagement,delRuleManagement,getReadingStatusByRuleId,getReadingStatusList,addReadingStatus,updateReadingStatus  } from '@/api/collaborativeApproval/sealManagement.js'
import { el } from 'element-plus/es/locales.mjs'
import { getUserProfile } from '@/api/system/user.js'
import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js";
import useUserStore from '@/store/modules/user'
import { userLoginFacotryList } from "@/api/system/user.js"
  import { ref, reactive, onMounted, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { Plus } from "@element-plus/icons-vue";
  import {
    listSealApplication,
    addSealApplication,
    updateSealApplication,
    listRuleManagement,
    addRuleManagement,
    updateRuleManagement,
    delRuleManagement,
    getReadingStatusByRuleId,
    getReadingStatusList,
    addReadingStatus,
    updateReadingStatus,
  } from "@/api/collaborativeApproval/sealManagement.js";
  import { el } from "element-plus/es/locales.mjs";
  import { getUserProfile } from "@/api/system/user.js";
  import {
    staffJoinDel,
    staffJoinListPage,
  } from "@/api/personnelManagement/onboarding.js";
  import useUserStore from "@/store/modules/user";
  import { userLoginFacotryList } from "@/api/system/user.js";
// 响应式数据
const currentUser = ref(null)
const activeTab = ref('seal')
const operationType = ref('add')
const tableData = ref([])
// 用印申请相关
const userStore = useUserStore()
const showSealApplyDialog = ref(false)
const tableLoading = ref(false)
const showSealDetailDialog = ref(false)
const currentSealDetail = ref(null)
const sealFormRef = ref()
const sealForm = reactive({
  applicationNum: '',
  title: '',
  sealType: '',
  reason: '',
  urgency: 'normal',
  status: 'pending'
})
  // 响应式数据
  const currentUser = ref(null);
  const activeTab = ref("seal");
  const operationType = ref("add");
  const tableData = ref([]);
  // 用印申请相关
  const userStore = useUserStore();
  const showSealApplyDialog = ref(false);
  const tableLoading = ref(false);
  const showSealDetailDialog = ref(false);
  const currentSealDetail = ref(null);
  const sealFormRef = ref();
  const sealForm = reactive({
    applicationNum: "",
    title: "",
    sealType: "",
    reason: "",
    urgency: "normal",
    status: "pending",
  });
const sealRules = {
  applicationNum: [{ required: true, message: '请输入申请编号', trigger: 'blur' }],
  title: [{ required: true, message: '请输入申请标题', trigger: 'blur' }],
  sealType: [{ required: true, message: '请选择用印类型', trigger: 'change' }],
  reason: [{ required: true, message: '请输入申请原因', trigger: 'blur' }]
}
  const sealRules = {
    applicationNum: [
      { required: true, message: "请输入申请编号", trigger: "blur" },
    ],
    title: [{ required: true, message: "请输入申请标题", trigger: "blur" }],
    sealType: [{ required: true, message: "请选择用印类型", trigger: "change" }],
    reason: [{ required: true, message: "请输入申请原因", trigger: "blur" }],
  };
const sealSearchForm = reactive({
  title: '',
  status: ''
})
// 分页参数
const page = reactive({
  current: 1,
  size: 10,
  total: 0
})
// 规章制度相关
const showRegulationDialog = ref(false)
const showRegulationDetailDialog = ref(false)
const showVersionHistoryDialog = ref(false)
const showReadStatusDialog = ref(false)
const currentRegulationDetail = ref(null)
const regulationFormRef = ref()
const regulationForm = reactive({
  id: '',
  regulationNum: '',
  title: '',
  category: '',
  content: '',
  version: '',
  status: 'active',
  readCount: 0,
  effectiveTime: '',
  scope: [],
  requireConfirm: false
})
  const sealSearchForm = reactive({
    title: "",
    status: "",
  });
  // 分页参数
  const page = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  // 规章制度相关
  const showRegulationDialog = ref(false);
  const showRegulationDetailDialog = ref(false);
  const showVersionHistoryDialog = ref(false);
  const showReadStatusDialog = ref(false);
  const currentRegulationDetail = ref(null);
  const regulationFormRef = ref();
  const regulationForm = reactive({
    id: "",
    regulationNum: "",
    title: "",
    category: "",
    content: "",
    version: "",
    status: "active",
    readCount: 0,
    effectiveTime: "",
    scope: [],
    requireConfirm: false,
  });
const readStatus = ref({
  id: '',
  ruleId: '',
  employee: '',
  department: '',
  createTime: '',
  confirmTime: '',
  status: 'unconfirmed'
})
  const readStatus = ref({
    id: "",
    ruleId: "",
    employee: "",
    department: "",
    createTime: "",
    confirmTime: "",
    status: "unconfirmed",
  });
const regulationRules = {
  title: [{ required: true, message: '请输入制度标题', trigger: 'blur' }],
  category: [{ required: true, message: '请选择制度分类', trigger: 'change' }],
  content: [{ required: true, message: '请输入制度内容', trigger: 'blur' }],
  effectiveTime: [{ required: true, message: '请选择生效时间', trigger: 'change' }],
  scope: [{ required: true, message: '请选择适用范围', trigger: 'change' }]
}
  const regulationRules = {
    title: [{ required: true, message: "请输入制度标题", trigger: "blur" }],
    category: [{ required: true, message: "请选择制度分类", trigger: "change" }],
    content: [{ required: true, message: "请输入制度内容", trigger: "blur" }],
    effectiveTime: [
      { required: true, message: "请选择生效时间", trigger: "change" },
    ],
    scope: [{ required: true, message: "请选择适用范围", trigger: "change" }],
  };
const regulationSearchForm = reactive({
  title: '',
  category: ''
})
  const regulationSearchForm = reactive({
    title: "",
    category: "",
  });
// 假数据
const sealApplications = ref([])
  // 假数据
  const sealApplications = ref([]);
const regulations = ref([])
  const regulations = ref([]);
const versionHistory = ref([])
  const versionHistory = ref([]);
const readStatusList = ref([])
  const readStatusList = ref([]);
  // { employee: '陈志强', department: '销售部', readTime: '2025-01-11 10:30:00', confirmTime: '2025-01-11 10:35:00', status: 'confirmed' },
  // { employee: '刘雅婷', department: '技术部', readTime: '2025-01-11 14:20:00', confirmTime: '', status: 'unconfirmed' },
  // { employee: '王建国', department: '财务部', readTime: '2025-01-12 09:15:00', confirmTime: '2025-01-12 09:20:00', status: 'confirmed' }
// 用印申请状态
const getStatusType = (status) => {
  const statusMap = {
    pending: 'warning',
    approved: 'success',
    rejected: 'danger'
  }
  return statusMap[status] || 'info'
}
// 制度状态
const getStatusText = (status) => {
  const statusMap = {
    pending: '待审批',
    approved: '已通过',
    rejected: '已拒绝'
  }
  return statusMap[status] || '未知'
}
// 用印类型
const getSealTypeText = (sealType) => {
  const sealTypeMap = {
    official: '公章',
    contract: '合同专用章',
    finance: '财务专用章',
    tegal: '技术专用章'
  }
  return sealTypeMap[sealType] || '未知'
}
// 制度分类
const getCategoryText = (category) => {
  const categoryMap = {
    hr: '人事制度',
    finance: '财务制度',
    safety: '安全制度',
    tech: '技术制度'
  }
  return categoryMap[category] || '未知'
}
// 搜索印章申请
const searchSealApplications = () => {
  page.current=1
  getSealApplicationList()
  // 用印申请状态
  const getStatusType = status => {
    const statusMap = {
      pending: "warning",
      approved: "success",
      rejected: "danger",
    };
    return statusMap[status] || "info";
  };
  // 制度状态
  const getStatusText = status => {
    const statusMap = {
      pending: "待审批",
      approved: "已通过",
      rejected: "已拒绝",
    };
    return statusMap[status] || "未知";
  };
  // 用印类型
  const getSealTypeText = sealType => {
    const sealTypeMap = {
      official: "公章",
      contract: "合同专用章",
      finance: "财务专用章",
      tegal: "技术专用章",
    };
    return sealTypeMap[sealType] || "未知";
  };
  // 制度分类
  const getCategoryText = category => {
    const categoryMap = {
      hr: "人事制度",
      finance: "财务制度",
      safety: "安全制度",
      tech: "技术制度",
    };
    return categoryMap[category] || "未知";
  };
  // 搜索印章申请
  const searchSealApplications = () => {
    page.current = 1;
    getSealApplicationList();
  // ElMessage.success('搜索完成')
}
// 重置印章申请搜索
const resetSealSearch = () => {
  sealSearchForm.title = ''
  sealSearchForm.status = ''
  searchSealApplications()
}
// 搜索制度
const searchRegulations = () => {
  page.current=1
  getRegulationList()
}
// 重置制度搜索
const resetRegulationSearch = () => {
  regulationSearchForm.title = ''
  regulationSearchForm.category = ''
  searchRegulations()
}
// 提交用印申请
const submitSealApplication = async () => {
  try {
    await sealFormRef.value.validate()
    addSealApplication(sealForm).then(res => {
      if(res.code == 200){
        ElMessage.success('申请提交成功')
        showSealApplyDialog.value = false
        getSealApplicationList()
        Object.assign(sealForm, {
        applicationNum: '',
        title: '',
        sealType: '',
        reason: '',
        urgency: 'normal',
        status: 'pending'
    // ElMessage.success('搜索完成')
  };
  // 重置印章申请搜索
  const resetSealSearch = () => {
    sealSearchForm.title = "";
    sealSearchForm.status = "";
    searchSealApplications();
  };
  // 搜索制度
  const searchRegulations = () => {
    page.current = 1;
    getRegulationList();
  };
  // 重置制度搜索
  const resetRegulationSearch = () => {
    regulationSearchForm.title = "";
    regulationSearchForm.category = "";
    searchRegulations();
  };
  // 提交用印申请
  const submitSealApplication = async () => {
    try {
      await sealFormRef.value.validate();
      addSealApplication(sealForm)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("申请提交成功");
            showSealApplyDialog.value = false;
            getSealApplicationList();
            Object.assign(sealForm, {
              applicationNum: "",
              title: "",
              sealType: "",
              reason: "",
              urgency: "normal",
              status: "pending",
            });
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
    } catch (error) {
      ElMessage.error("请完善申请信息");
    }
  };
  // 新增
  const handleAdd = () => {
    operationType.value = "add";
    resetRegulationForm();
    showRegulationDialog.value = true;
  };
  // 编辑
  const handleEdit = row => {
    operationType.value = "edit";
    Object.assign(regulationForm, row);
    showRegulationDialog.value = true;
  };
  // 废弃
  const repealEdit = row => {
    operationType.value = "edit";
    Object.assign(regulationForm, row);
    regulationForm.status = "repealed";
    ElMessageBox.confirm("确认废弃该制度?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        updateRuleManagement(regulationForm).then(res => {
          if (res.code == 200) {
            ElMessage.success("制度废弃成功");
            // showRegulationDialog.value = false
            getRegulationList();
            resetRegulationForm();
          }
        });
      })
      .catch(() => {
        ElMessage({
          type: "info",
          message: "已取消废弃",
        });
      });
  };
  // 发布制度
  const submitRegulation = async () => {
    try {
      await regulationFormRef.value.validate();
      if (operationType.value == "add") {
        addRuleManagement(regulationForm).then(res => {
          if (res.code == 200) {
            ElMessage.success("制度发布成功");
            showRegulationDialog.value = false;
            getRegulationList();
            resetRegulationForm();
          }
        });
      } else {
        updateRuleManagement(regulationForm).then(res => {
          if (res.code == 200) {
            ElMessage.success("制度编辑成功");
            showRegulationDialog.value = false;
            resetRegulationForm();
            getRegulationList();
          }
        });
      }
    }).catch(err => {
      ElMessage.error(err.msg)
    })
  } catch (error) {
    ElMessage.error('请完善申请信息')
  }
}
// 新增
const handleAdd = () => {
  operationType.value = 'add'
  resetRegulationForm()
  showRegulationDialog.value = true
}
    } catch (err) {
      ElMessage.error(err.msg);
    }
  };
  //重置制度表单
  const resetRegulationForm = () => {
    Object.assign(regulationForm, {
      id: "",
      regulationNum: "",
      title: "",
      category: "",
      content: "",
      version: "",
      status: "active",
      readCount: 0,
      effectiveTime: "",
      scope: [],
      requireConfirm: false,
    });
  };
// 编辑
const handleEdit = (row) => {
  operationType.value = 'edit'
  Object.assign(regulationForm, row)
  showRegulationDialog.value = true
}
// 废弃
const repealEdit = (row) => {
  operationType.value = 'edit'
  Object.assign(regulationForm, row)
  regulationForm.status = 'repealed'
  ElMessageBox.confirm('确认废弃该制度?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    updateRuleManagement(regulationForm).then(res => {
      if(res.code == 200){
        ElMessage.success('制度废弃成功')
        // showRegulationDialog.value = false
        getRegulationList()
        resetRegulationForm()
  // 查看用印申请详情
  const viewSealDetail = row => {
    currentSealDetail.value = row;
    showSealDetailDialog.value = true;
  };
  // 审批用印申请
  const approveSeal = row => {
    console.log(row);
    ElMessageBox.confirm("确认通过该用印申请?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      row.status = "approved";
      updateSealApplication(row).then(res => {
        if (res.code == 200) {
          ElMessage.success("审批通过");
        }
      });
    });
  };
  // 拒绝用印申请
  const rejectSeal = row => {
    ElMessageBox.prompt("请输入拒绝原因", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      inputPattern: /\S+/,
      inputErrorMessage: "拒绝原因不能为空",
    }).then(({ value }) => {
      row.status = "rejected";
      updateSealApplication(row).then(res => {
        if (res.code == 200) {
          ElMessage.success("审批拒绝");
        }
      });
      ElMessage.success("已拒绝申请");
    });
  };
  // 获取在职员工列表
  const getList = () => {
    tableLoading.value = true;
    //获取当前登录用户信息
    getUserProfile().then(res => {
      if (res.code == 200) {
        console.log(res.data.userName);
        currentUser.value = res.data.userName;
      }
    })
  }).catch(() => {
    ElMessage({
      type: 'info',
      message: '已取消废弃'
    })
  })
}
// 发布制度
const submitRegulation = async () => {
  try {
    await regulationFormRef.value.validate()
    if(operationType.value == 'add'){
      addRuleManagement(regulationForm).then(res => {
        if(res.code == 200){
          ElMessage.success('制度发布成功')
          showRegulationDialog.value = false
          getRegulationList()
          resetRegulationForm()
    });
    staffJoinListPage({ staffState: 1 })
      .then(res => {
        tableLoading.value = false;
        // tableData.value = res.data.records
        // //筛选出和currentUser同名的人员
        tableData.value = res.data.records.filter(
          item => item.staffName === currentUser.value
        );
        console.log("tableData", tableData.value);
        page.total = res.data.total;
        if (tableData.value.length == 0) {
          ElMessage.error("当前用户未加入任何部门");
        }
      })
    }else{
      updateRuleManagement(regulationForm).then(res => {
        if(res.code == 200){
          ElMessage.success('制度编辑成功')
          showRegulationDialog.value = false
          resetRegulationForm()
          getRegulationList()
      }})}
  }catch(err){
    ElMessage.error(err.msg)
  }
}
//重置制度表单
const resetRegulationForm = () => {
  Object.assign(regulationForm, {
    id: '',
    regulationNum: '',
    title: '',
    category: '',
    content: '',
    version: '',
    status: 'active',
    readCount: 0,
    effectiveTime: '',
    scope: [],
    requireConfirm: false
})
}
      .catch(err => {
        tableLoading.value = false;
      });
  };
// 查看用印申请详情
const viewSealDetail = (row) => {
  currentSealDetail.value = row
  showSealDetailDialog.value = true
}
// 审批用印申请
const approveSeal = (row) => {
  console.log(row)
  ElMessageBox.confirm('确认通过该用印申请?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    row.status = 'approved'
    updateSealApplication(row).then(res => {
      if(res.code == 200){
        ElMessage.success('审批通过')
  // 查看制度版本历史
  const viewVersionHistory = row => {
    showVersionHistoryDialog.value = true;
    const params = {
      category: row.category,
    };
    listRuleManagement(page, params).then(res => {
      if (res.code == 200) {
        versionHistory.value = res.data.records;
      }
    })
  })
}
// 拒绝用印申请
const rejectSeal = (row) => {
  ElMessageBox.prompt('请输入拒绝原因', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    inputPattern: /\S+/,
    inputErrorMessage: '拒绝原因不能为空'
  }).then(({ value }) => {
    row.status = 'rejected'
    updateSealApplication(row).then(res => {
      if(res.code == 200){
        ElMessage.success('审批拒绝')
      }
    })
    ElMessage.success('已拒绝申请')
  })
}
// 获取在职员工列表
const getList = () => {
  tableLoading.value = true;
      //获取当前登录用户信息
  getUserProfile().then(res => {
    if(res.code == 200){
      console.log(res.data.userName)
      currentUser.value = res.data.userName
    }
  })
  staffJoinListPage({staffState: 1}).then(res => {
    tableLoading.value = false;
    // tableData.value = res.data.records
    // //筛选出和currentUser同名的人员
    tableData.value = res.data.records.filter(item => item.staffName === currentUser.value)
    console.log("tableData",tableData.value)
    page.total = res.data.total;
    if(tableData.value.length == 0){
    ElMessage.error('当前用户未加入任何部门')
    }
  }).catch(err => {
    tableLoading.value = false;
  })
};
// 查看制度版本历史
const viewVersionHistory = (row) => {
  showVersionHistoryDialog.value = true
  const params = {
    category: row.category
  }
  listRuleManagement(page,params).then(res => {
    if(res.code == 200){
      versionHistory.value = res.data.records
    }
  })
}
// 查看制度详情
const viewRegulation = (row) => {
  getList()
  currentRegulationDetail.value = row
  showRegulationDetailDialog.value = true
  getReadingStatusByRuleId(row.id).then(res => {
    if(res.code == 200){
      readStatusList.value = res.data
      if(readStatusList.value.length==0 && tableData.value.length>0){
    });
  };
  // 查看制度详情
  const viewRegulation = row => {
    getList();
    currentRegulationDetail.value = row;
    showRegulationDetailDialog.value = true;
    getReadingStatusByRuleId(row.id).then(res => {
      if (res.code == 200) {
        readStatusList.value = res.data;
        if (readStatusList.value.length == 0 && tableData.value.length > 0) {
          const params = {
          ruleId: row.id,
          employee: tableData.value[0].staffName,
          department: tableData.value[0].postJob,
          status: 'unconfirmed'
            ruleId: row.id,
            employee: tableData.value[0].staffName,
            department: tableData.value[0].postJob,
            status: "unconfirmed",
          };
          addReadingStatus(params).then(res => {
            if (res.code == 200) {
              ElMessage.success("制度阅读成功");
            }
          });
        }
        addReadingStatus(params).then(res => {
          if(res.code == 200){
            ElMessage.success('制度阅读成功')
          }
        })
      }
    }
  })
}
// 查看制度阅读状态
const viewReadStatus = (row) => {
  showReadStatusDialog.value = true
  //查看阅读状态列表
  getReadingStatusByRuleId(row.id).then(res => {
    if(res.code == 200){
      readStatusList.value = res.data
    }
  })
}
//确认查看
const resetForm = (row) => {
  console.log("row",row)
  row.readCount = row.readCount + 1
  updateRuleManagement(row).then(res => {
    if(res.code == 200){
      ElMessage.success('查看数量修改成功')
      //修改阅读状态
      //根据制度id和当前登录的员工得到阅读状态
      // let item = readStatusList.value.filter(item => item.employee == tableData.value[0].staffName )
      // if(item.length>0){
      //   item[0].status = 'confirmed',
      //   item[0].confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
      // }
      // 筛选当前员工对应该制度的阅读状态记录
      let statusItem = readStatusList.value.find(item => item.employee === tableData.value[0].staffName && item.ruleId === row.id);
      if (statusItem) {
        // 如果找到记录,更新状态和确认时间
        statusItem.status = 'confirmed';
        // 格式化时间为"YYYY-MM-DD HH:mm:ss"格式
        const now = new Date();
        statusItem.confirmTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
        // statusItem.confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
        updateReadingStatus(statusItem).then(res => {
          if(res.code == 200){
            ElMessage.success('制度阅读状态修改成功')
          }
        })
    });
  };
  // 查看制度阅读状态
  const viewReadStatus = row => {
    showReadStatusDialog.value = true;
    //查看阅读状态列表
    getReadingStatusByRuleId(row.id).then(res => {
      if (res.code == 200) {
        readStatusList.value = res.data;
      }
    });
  };
    }
  })
}
  //确认查看
  const resetForm = row => {
    console.log("row", row);
    row.readCount = row.readCount + 1;
// 导出规章制度
const { proxy } = getCurrentInstance()
const handleExport = () => {
  proxy.download('/rulesRegulationsManagement/export', { ...regulationSearchForm }, '规章制度.xlsx')
}
    updateRuleManagement(row).then(res => {
      if (res.code == 200) {
        ElMessage.success("查看数量修改成功");
        //修改阅读状态
        //根据制度id和当前登录的员工得到阅读状态
        // let item = readStatusList.value.filter(item => item.employee == tableData.value[0].staffName )
        // if(item.length>0){
        //   item[0].status = 'confirmed',
        //   item[0].confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
        // }
        // 筛选当前员工对应该制度的阅读状态记录
        let statusItem = readStatusList.value.find(
          item =>
            item.employee === tableData.value[0].staffName &&
            item.ruleId === row.id
        );
// 获取印章申请列表数据
const getSealApplicationList = async () => {
  tableLoading.value = true
  listSealApplication(page,sealSearchForm)
  .then(res => {
    //获取当前登录的部门信息
// 获取当前登录的部门信息并过滤数据
    const currentFactoryName = userStore.currentFactoryName
    if (currentFactoryName) {
      // 根据currentFactoryName过滤出department相同的数据
      sealApplications.value = res.data.records.filter(item => item.department === currentFactoryName)
      // 更新过滤后的总数
      page.value.total = sealApplications.value.length
    } else {
      // 如果没有currentFactoryName,则显示所有数据
      sealApplications.value = res.data.records
      page.value.total = res.data.total
    }
    // sealApplications.value = res.data.records
    // page.value.total = res.data.total;
    tableLoading.value = false;
        if (statusItem) {
          // 如果找到记录,更新状态和确认时间
          statusItem.status = "confirmed";
          // 格式化时间为"YYYY-MM-DD HH:mm:ss"格式
          const now = new Date();
          statusItem.confirmTime = `${now.getFullYear()}-${String(
            now.getMonth() + 1
          ).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")} ${String(
            now.getHours()
          ).padStart(2, "0")}:${String(now.getMinutes()).padStart(
            2,
            "0"
          )}:${String(now.getSeconds()).padStart(2, "0")}`;
          // statusItem.confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
  }).catch(err => {
    tableLoading.value = false;
  })
}
// 获取规章制度列表数据
const getRegulationList = async () => {
  tableLoading.value = true
  listRuleManagement(page,regulationSearchForm)
  .then(res => {
          updateReadingStatus(statusItem).then(res => {
            if (res.code == 200) {
              ElMessage.success("制度阅读状态修改成功");
            }
          });
        }
      }
    });
  };
    regulations.value = res.data.records
    // 过滤掉已废弃的制度
    // regulations.value = res.data.records.filter(item => item.status !== 'repealed')
    page.value.total = res.data.total;
    tableLoading.value = false;
  // 导出规章制度
  const { proxy } = getCurrentInstance();
  const handleExport = () => {
    proxy.download(
      "/rulesRegulationsManagement/export",
      { ...regulationSearchForm },
      "规章制度.xlsx"
    );
  };
  }).catch(err => {
    tableLoading.value = false;
  })
}
  // 获取印章申请列表数据
  const getSealApplicationList = async () => {
    tableLoading.value = true;
    listSealApplication(page, sealSearchForm)
      .then(res => {
        //获取当前登录的部门信息
        // 获取当前登录的部门信息并过滤数据
        const currentFactoryName = userStore.currentFactoryName;
        if (currentFactoryName) {
          // 根据currentFactoryName过滤出department相同的数据
          sealApplications.value = res.data.records.filter(
            item => item.department === currentFactoryName
          );
          // 更新过滤后的总数
          page.value.total = sealApplications.value.length;
        } else {
          // 如果没有currentFactoryName,则显示所有数据
          sealApplications.value = res.data.records;
          page.value.total = res.data.total;
        }
        // sealApplications.value = res.data.records
        // page.value.total = res.data.total;
        tableLoading.value = false;
      })
      .catch(err => {
        tableLoading.value = false;
      });
  };
  // 获取规章制度列表数据
  const getRegulationList = async () => {
    tableLoading.value = true;
    listRuleManagement(page, regulationSearchForm)
      .then(res => {
        regulations.value = res.data.records;
        // 过滤掉已废弃的制度
        // regulations.value = res.data.records.filter(item => item.status !== 'repealed')
        page.value.total = res.data.total;
        tableLoading.value = false;
      })
      .catch(err => {
        tableLoading.value = false;
      });
  };
onMounted(() => {
  // 初始化
  getSealApplicationList()
  getRegulationList()
})
  onMounted(() => {
    // 初始化
    getSealApplicationList();
    getRegulationList();
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
}
  .app-container {
    padding: 20px;
  }
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
.tab-content {
  padding: 20px 0;
}
  .tab-content {
    padding: 20px 0;
  }
.mb-20 {
  margin-bottom: 20px;
}
  .mb-20 {
    margin-bottom: 20px;
  }
.mt-20 {
  margin-top: 20px;
}
  .mt-20 {
    margin-top: 20px;
  }
.ml-10 {
  margin-left: 10px;
}
  .ml-10 {
    margin-left: 10px;
  }
.regulation-content {
  background-color: #f5f5f5;
  padding: 15px;
  border-radius: 4px;
  line-height: 1.6;
  white-space: pre-wrap;
  height: 200px;
}
  .regulation-content {
    background-color: #f5f5f5;
    padding: 15px;
    border-radius: 4px;
    line-height: 1.6;
    white-space: pre-wrap;
    height: 200px;
  }
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
  }
</style>
src/views/collaborativeApproval/sealManagement/index.vue
@@ -6,98 +6,145 @@
          <span>用印管理发布</span>
        </div>
      </template>
   <!-- 用印申请管理 -->
        <div class="tab-content">
            <el-row :gutter="20" class="mb-20 ">
              <span class="ml-10">用印标题:</span>
              <el-col :span="6">
                <el-input v-model="sealSearchForm.title" placeholder="请输入申请标题" clearable />
              </el-col>
              <span class="search_title">审批状态:</span>
              <el-col :span="6">
                <el-select v-model="sealSearchForm.status" placeholder="审批状态" clearable>
                  <el-option label="待审批" value="pending" />
                  <el-option label="已通过" value="approved" />
                  <el-option label="已拒绝" value="rejected" />
                </el-select>
              </el-col>
              <el-col :span="8">
                <el-button type="primary" @click="searchSealApplications">搜索</el-button>
                <el-button @click="resetSealSearch">重置</el-button>
                <el-button @click="handleExport">导出</el-button>
                <el-button type="primary" @click="showSealApplyDialog = true">申请用印
                </el-button>
              </el-col>
            </el-row>
            <el-table :data="sealApplications" border v-loading="tableLoading" style="width: 100%">
              <el-table-column prop="applicationNum" label="申请编号" width="120" />
              <el-table-column prop="title" label="申请标题" min-width="200" />
              <el-table-column prop="createUserName" label="申请人" width="120" />
              <el-table-column prop="department" label="所属部门" width="150" />
              <el-table-column prop="sealType" label="用印类型" width="120">
                <template #default="scope">
                  {{ getSealTypeText(scope.row.sealType) }}
                </template>
              </el-table-column>
              <el-table-column prop="createTime" label="申请时间" width="180" />
              <el-table-column prop="status" label="状态" width="100">
                <template #default="scope">
                  <el-tag :type="getStatusType(scope.row.status)">
                    {{ getStatusText(scope.row.status) }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="操作" width="200" fixed="right">
                <template #default="scope">
                  <el-button link @click="viewSealDetail(scope.row)">查看</el-button>
                  <el-button
                    v-if="scope.row.status === 'pending'"
                                        link
                    type="primary"
                    @click="approveSeal(scope.row)"
                  >
                    审批
                  </el-button>
                  <el-button
                    v-if="scope.row.status === 'pending'"
                                        link
                    type="danger"
                    @click="rejectSeal(scope.row)"
                  >
                    拒绝
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
                    <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
                                            :page="page.current" :limit="page.size" @pagination="paginationChange" />
        </div>
      <!-- 用印申请管理 -->
      <div class="tab-content">
        <el-row :gutter="20"
                class="mb-20 ">
          <span class="ml-10">用印标题:</span>
          <el-col :span="6">
            <el-input v-model="sealSearchForm.title"
                      placeholder="请输入申请标题"
                      clearable />
          </el-col>
          <span class="search_title">审批状态:</span>
          <el-col :span="6">
            <el-select v-model="sealSearchForm.status"
                       placeholder="审批状态"
                       clearable>
              <el-option label="待审批"
                         value="pending" />
              <el-option label="已通过"
                         value="approved" />
              <el-option label="已拒绝"
                         value="rejected" />
            </el-select>
          </el-col>
          <el-col :span="8">
            <el-button type="primary"
                       @click="searchSealApplications">搜索</el-button>
            <el-button @click="resetSealSearch">重置</el-button>
            <el-button @click="handleExport">导出</el-button>
            <el-button type="primary"
                       @click="showSealApplyDialog = true">申请用印
            </el-button>
          </el-col>
        </el-row>
        <el-table :data="sealApplications"
                  border
                  v-loading="tableLoading"
                  style="width: 100%">
          <el-table-column prop="applicationNum"
                           label="申请编号"
                           width="120" />
          <el-table-column prop="title"
                           label="申请标题"
                           min-width="200" />
          <el-table-column prop="createUserName"
                           label="申请人"
                           width="120" />
          <el-table-column prop="department"
                           label="所属部门"
                           width="150" />
          <el-table-column prop="sealType"
                           label="用印类型"
                           width="120">
            <template #default="scope">
              {{ getSealTypeText(scope.row.sealType) }}
            </template>
          </el-table-column>
          <el-table-column prop="createTime"
                           label="申请时间"
                           width="180" />
          <el-table-column prop="status"
                           label="状态"
                           width="100">
            <template #default="scope">
              <el-tag :type="getStatusType(scope.row.status)">
                {{ getStatusText(scope.row.status) }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="操作"
                           width="200"
                           fixed="right">
            <template #default="scope">
              <el-button link
                         @click="viewSealDetail(scope.row)">查看</el-button>
              <el-button v-if="scope.row.status === 'pending'"
                         link
                         type="primary"
                         @click="approveSeal(scope.row)">
                审批
              </el-button>
              <el-button v-if="scope.row.status === 'pending'"
                         link
                         type="danger"
                         @click="rejectSeal(scope.row)">
                拒绝
              </el-button>
            </template>
          </el-table-column>
        </el-table>
        <pagination v-show="total > 0"
                    :total="total"
                    layout="total, sizes, prev, pager, next, jumper"
                    :page="page.current"
                    :limit="page.size"
                    @pagination="paginationChange" />
      </div>
    </el-card>
    <!-- 用印申请对话框 -->
    <el-dialog v-model="showSealApplyDialog" title="申请用印" width="600px">
      <el-form :model="sealForm" :rules="sealRules" ref="sealFormRef" label-width="100px">
        <el-form-item label="申请编号" prop="applicationNum">
          <el-input v-model="sealForm.applicationNum" placeholder="请输入申请编号" />
    <el-dialog v-model="showSealApplyDialog"
               title="申请用印"
               width="600px">
      <el-form :model="sealForm"
               :rules="sealRules"
               ref="sealFormRef"
               label-width="100px">
        <el-form-item label="申请编号"
                      prop="applicationNum">
          <el-input v-model="sealForm.applicationNum"
                    placeholder="请输入申请编号" />
        </el-form-item>
        <el-form-item label="申请标题" prop="title">
          <el-input v-model="sealForm.title" placeholder="请输入申请标题" />
        <el-form-item label="申请标题"
                      prop="title">
          <el-input v-model="sealForm.title"
                    placeholder="请输入申请标题" />
        </el-form-item>
        <el-form-item label="用印类型" prop="sealType">
          <el-select v-model="sealForm.sealType" placeholder="请选择用印类型" style="width: 100%">
            <el-option label="公章" value="official" />
            <el-option label="合同专用章" value="contract" />
            <el-option label="财务专用章" value="finance" />
            <el-option label="法人章" value="legal" />
        <el-form-item label="用印类型"
                      prop="sealType">
          <el-select v-model="sealForm.sealType"
                     placeholder="请选择用印类型"
                     style="width: 100%">
            <el-option label="公章"
                       value="official" />
            <el-option label="合同专用章"
                       value="contract" />
            <el-option label="财务专用章"
                       value="finance" />
            <el-option label="法人章"
                       value="legal" />
          </el-select>
        </el-form-item>
        <el-form-item label="申请原因" prop="reason">
          <el-input v-model="sealForm.reason" type="textarea" :rows="4" placeholder="请详细说明用印原因" />
        <el-form-item label="申请原因"
                      prop="reason">
          <el-input v-model="sealForm.reason"
                    type="textarea"
                    :rows="4"
                    placeholder="请详细说明用印原因" />
        </el-form-item>
        <el-form-item label="紧急程度" prop="urgency">
        <el-form-item label="紧急程度"
                      prop="urgency">
          <el-radio-group v-model="sealForm.urgency">
            <el-radio label="normal">普通</el-radio>
            <el-radio label="urgent">紧急</el-radio>
@@ -107,12 +154,12 @@
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="submitSealApplication">提交申请</el-button>
          <el-button @click="showSealApplyDialog = false">取消</el-button>
          <el-button type="primary" @click="submitSealApplication">提交申请</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 规章制度发布对话框 -->
    <!-- <el-dialog v-model="showRegulationDialog" :title="operationType === 'add' ? '发布制度' : '编辑制度'" width="800px">
      <el-form :model="regulationForm" :rules="regulationRules" ref="regulationFormRef" label-width="100px">
@@ -161,11 +208,14 @@
        </span>
      </template>
    </el-dialog> -->
    <!-- 用印详情对话框 -->
    <el-dialog v-model="showSealDetailDialog" title="用印申请详情" width="700px">
      <div v-if="currentSealDetail" class="mb10">
        <el-descriptions :column="2" border>
    <el-dialog v-model="showSealDetailDialog"
               title="用印申请详情"
               width="700px">
      <div v-if="currentSealDetail"
           class="mb10">
        <el-descriptions :column="2"
                         border>
          <el-descriptions-item label="申请编号">{{ currentSealDetail.id }}</el-descriptions-item>
          <el-descriptions-item label="申请标题">{{ currentSealDetail.title }}</el-descriptions-item>
          <el-descriptions-item label="申请人">{{ currentSealDetail.createUserName }}</el-descriptions-item>
@@ -177,15 +227,18 @@
              {{ getStatusText(currentSealDetail.status) }}
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="申请原因" :span="2">{{ currentSealDetail.reason }}</el-descriptions-item>
          <el-descriptions-item label="申请原因"
                                :span="2">{{ currentSealDetail.reason }}</el-descriptions-item>
        </el-descriptions>
      </div>
    </el-dialog>
    <!-- 规章制度详情对话框 -->
    <el-dialog v-model="showRegulationDetailDialog" title="规章制度详情" width="800px">
    <el-dialog v-model="showRegulationDetailDialog"
               title="规章制度详情"
               width="800px">
      <div v-if="currentRegulationDetail">
        <el-descriptions :column="2" border>
        <el-descriptions :column="2"
                         border>
          <el-descriptions-item label="制度编号">{{ currentRegulationDetail.id }}</el-descriptions-item>
          <el-descriptions-item label="制度标题">{{ currentRegulationDetail.title }}</el-descriptions-item>
          <el-descriptions-item label="分类">{{ getCategoryText(currentRegulationDetail.category) }}</el-descriptions-item>
@@ -198,19 +251,30 @@
          <div class="regulation-content">{{ currentRegulationDetail.content }}</div>
        </div>
        <!-- 如果tableData>0 显示 -->
        <div style="margin: 10px 0;" v-if="tableData && tableData.length > 0" >
          <el-button type="success" @click="resetForm(currentRegulationDetail)">确认查看</el-button>
        <div style="margin: 10px 0;"
             v-if="tableData && tableData.length > 0">
          <el-button type="success"
                     @click="resetForm(currentRegulationDetail)">确认查看</el-button>
        </div>
      </div>
    </el-dialog>
    <!-- 版本历史对话框 -->
    <el-dialog v-model="showVersionHistoryDialog" title="版本历史" width="800px">
      <el-table :data="versionHistory" style="width: 100%;margin-bottom: 10px">
        <el-table-column prop="version" label="版本号" width="100" />
        <el-table-column prop="updateTime" label="更新时间" width="180" />
        <el-table-column prop="createUserName" label="更新人" width="120" />
        <el-table-column prop="changeLog" label="变更说明">
    <el-dialog v-model="showVersionHistoryDialog"
               title="版本历史"
               width="800px">
      <el-table :data="versionHistory"
                style="width: 100%;margin-bottom: 10px">
        <el-table-column prop="version"
                         label="版本号"
                         width="100" />
        <el-table-column prop="updateTime"
                         label="更新时间"
                         width="180" />
        <el-table-column prop="createUserName"
                         label="更新人"
                         width="120" />
        <el-table-column prop="changeLog"
                         label="变更说明">
          <template #default="scope">
            <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
              {{ scope.row.status === 'active' ? '生效中' : '已废止' }}
@@ -219,15 +283,27 @@
        </el-table-column>
      </el-table>
    </el-dialog>
    <!-- 阅读状态对话框 -->
    <el-dialog v-model="showReadStatusDialog" title="阅读状态" width="800px">
      <el-table :data="readStatusList" style="width: 100%;margin-bottom: 10px">
        <el-table-column prop="employee" label="员工姓名" width="120" />
        <el-table-column prop="department" label="所属部门" width="150" />
        <el-table-column prop="createTime" label="阅读时间" width="180" />
        <el-table-column prop="confirmTime" label="确认时间" width="180" />
        <el-table-column prop="status" label="状态" width="100">
    <el-dialog v-model="showReadStatusDialog"
               title="阅读状态"
               width="800px">
      <el-table :data="readStatusList"
                style="width: 100%;margin-bottom: 10px">
        <el-table-column prop="employee"
                         label="员工姓名"
                         width="120" />
        <el-table-column prop="department"
                         label="所属部门"
                         width="150" />
        <el-table-column prop="createTime"
                         label="阅读时间"
                         width="180" />
        <el-table-column prop="confirmTime"
                         label="确认时间"
                         width="180" />
        <el-table-column prop="status"
                         label="状态"
                         width="100">
          <template #default="scope">
            <el-tag :type="scope.row.status === 'confirmed' ? 'success' : 'warning'">
              {{ scope.row.status === 'confirmed' ? '已确认' : '未确认' }}
@@ -240,525 +316,563 @@
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import { listSealApplication, addSealApplication, updateSealApplication,listRuleManagement,addRuleManagement,updateRuleManagement,delRuleManagement,getReadingStatusByRuleId,getReadingStatusList,addReadingStatus,updateReadingStatus  } from '@/api/collaborativeApproval/sealManagement.js'
import { el } from 'element-plus/es/locales.mjs'
import { getUserProfile } from '@/api/system/user.js'
import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js";
import useUserStore from '@/store/modules/user'
import { userLoginFacotryList } from "@/api/system/user.js"
  import { ref, reactive, onMounted, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { Plus } from "@element-plus/icons-vue";
  import {
    listSealApplication,
    addSealApplication,
    updateSealApplication,
    listRuleManagement,
    addRuleManagement,
    updateRuleManagement,
    delRuleManagement,
    getReadingStatusByRuleId,
    getReadingStatusList,
    addReadingStatus,
    updateReadingStatus,
  } from "@/api/collaborativeApproval/sealManagement.js";
  import { el } from "element-plus/es/locales.mjs";
  import { getUserProfile } from "@/api/system/user.js";
  import {
    staffJoinDel,
    staffJoinListPage,
  } from "@/api/personnelManagement/onboarding.js";
  import useUserStore from "@/store/modules/user";
  import { userLoginFacotryList } from "@/api/system/user.js";
// 响应式数据
const currentUser = ref(null)
const activeTab = ref('seal')
const operationType = ref('add')
const tableData = ref([])
// 用印申请相关
const userStore = useUserStore()
const showSealApplyDialog = ref(false)
const tableLoading = ref(false)
const showSealDetailDialog = ref(false)
const currentSealDetail = ref(null)
const sealFormRef = ref()
const sealForm = reactive({
  applicationNum: '',
  title: '',
  sealType: '',
  reason: '',
  urgency: 'normal',
  status: 'pending'
})
  // 响应式数据
  const currentUser = ref(null);
  const activeTab = ref("seal");
  const operationType = ref("add");
  const tableData = ref([]);
  // 用印申请相关
  const userStore = useUserStore();
  const showSealApplyDialog = ref(false);
  const tableLoading = ref(false);
  const showSealDetailDialog = ref(false);
  const currentSealDetail = ref(null);
  const sealFormRef = ref();
  const sealForm = reactive({
    applicationNum: "",
    title: "",
    sealType: "",
    reason: "",
    urgency: "normal",
    status: "pending",
  });
const sealRules = {
  applicationNum: [{ required: true, message: '请输入申请编号', trigger: 'blur' }],
  title: [{ required: true, message: '请输入申请标题', trigger: 'blur' }],
  sealType: [{ required: true, message: '请选择用印类型', trigger: 'change' }],
  reason: [{ required: true, message: '请输入申请原因', trigger: 'blur' }]
}
  const sealRules = {
    applicationNum: [
      { required: true, message: "请输入申请编号", trigger: "blur" },
    ],
    title: [{ required: true, message: "请输入申请标题", trigger: "blur" }],
    sealType: [{ required: true, message: "请选择用印类型", trigger: "change" }],
    reason: [{ required: true, message: "请输入申请原因", trigger: "blur" }],
  };
const sealSearchForm = reactive({
  title: '',
  status: ''
})
// 分页参数
const page = reactive({
  current: 1,
  size: 100,
  total: 0
})
// 规章制度相关
const showRegulationDialog = ref(false)
const showRegulationDetailDialog = ref(false)
const showVersionHistoryDialog = ref(false)
const showReadStatusDialog = ref(false)
const currentRegulationDetail = ref(null)
const regulationFormRef = ref()
const regulationForm = reactive({
  id: '',
  regulationNum: '',
  title: '',
  category: '',
  content: '',
  version: '',
  status: 'active',
  readCount: 0,
  effectiveTime: '',
  scope: [],
  requireConfirm: false
})
  const sealSearchForm = reactive({
    title: "",
    status: "",
  });
  // 分页参数
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  // 规章制度相关
  const showRegulationDialog = ref(false);
  const showRegulationDetailDialog = ref(false);
  const showVersionHistoryDialog = ref(false);
  const showReadStatusDialog = ref(false);
  const currentRegulationDetail = ref(null);
  const regulationFormRef = ref();
  const regulationForm = reactive({
    id: "",
    regulationNum: "",
    title: "",
    category: "",
    content: "",
    version: "",
    status: "active",
    readCount: 0,
    effectiveTime: "",
    scope: [],
    requireConfirm: false,
  });
const readStatus = ref({
  id: '',
  ruleId: '',
  employee: '',
  department: '',
  createTime: '',
  confirmTime: '',
  status: 'unconfirmed'
})
  const readStatus = ref({
    id: "",
    ruleId: "",
    employee: "",
    department: "",
    createTime: "",
    confirmTime: "",
    status: "unconfirmed",
  });
const regulationRules = {
  title: [{ required: true, message: '请输入制度标题', trigger: 'blur' }],
  category: [{ required: true, message: '请选择制度分类', trigger: 'change' }],
  content: [{ required: true, message: '请输入制度内容', trigger: 'blur' }],
  effectiveTime: [{ required: true, message: '请选择生效时间', trigger: 'change' }],
  scope: [{ required: true, message: '请选择适用范围', trigger: 'change' }]
}
  const regulationRules = {
    title: [{ required: true, message: "请输入制度标题", trigger: "blur" }],
    category: [{ required: true, message: "请选择制度分类", trigger: "change" }],
    content: [{ required: true, message: "请输入制度内容", trigger: "blur" }],
    effectiveTime: [
      { required: true, message: "请选择生效时间", trigger: "change" },
    ],
    scope: [{ required: true, message: "请选择适用范围", trigger: "change" }],
  };
const regulationSearchForm = reactive({
  title: '',
  category: ''
})
  const regulationSearchForm = reactive({
    title: "",
    category: "",
  });
// 假数据
const sealApplications = ref([])
  // 假数据
  const sealApplications = ref([]);
const regulations = ref([])
  const regulations = ref([]);
const versionHistory = ref([])
  const versionHistory = ref([]);
const readStatusList = ref([])
  const readStatusList = ref([]);
  // { employee: '陈志强', department: '销售部', readTime: '2025-01-11 10:30:00', confirmTime: '2025-01-11 10:35:00', status: 'confirmed' },
  // { employee: '刘雅婷', department: '技术部', readTime: '2025-01-11 14:20:00', confirmTime: '', status: 'unconfirmed' },
  // { employee: '王建国', department: '财务部', readTime: '2025-01-12 09:15:00', confirmTime: '2025-01-12 09:20:00', status: 'confirmed' }
// 用印申请状态
const getStatusType = (status) => {
  const statusMap = {
    pending: 'warning',
    approved: 'success',
    rejected: 'danger'
  }
  return statusMap[status] || 'info'
}
// 制度状态
const getStatusText = (status) => {
  const statusMap = {
    pending: '待审批',
    approved: '已通过',
    rejected: '已拒绝'
  }
  return statusMap[status] || '未知'
}
// 用印类型
const getSealTypeText = (sealType) => {
  const sealTypeMap = {
    official: '公章',
    contract: '合同专用章',
    finance: '财务专用章',
    tegal: '技术专用章'
  }
  return sealTypeMap[sealType] || '未知'
}
// 制度分类
const getCategoryText = (category) => {
  const categoryMap = {
    hr: '人事制度',
    finance: '财务制度',
    safety: '安全制度',
    tech: '技术制度'
  }
  return categoryMap[category] || '未知'
}
// 搜索印章申请
const searchSealApplications = () => {
  page.current=1
  getSealApplicationList()
  // 用印申请状态
  const getStatusType = status => {
    const statusMap = {
      pending: "warning",
      approved: "success",
      rejected: "danger",
    };
    return statusMap[status] || "info";
  };
  // 制度状态
  const getStatusText = status => {
    const statusMap = {
      pending: "待审批",
      approved: "已通过",
      rejected: "已拒绝",
    };
    return statusMap[status] || "未知";
  };
  // 用印类型
  const getSealTypeText = sealType => {
    const sealTypeMap = {
      official: "公章",
      contract: "合同专用章",
      finance: "财务专用章",
      tegal: "技术专用章",
    };
    return sealTypeMap[sealType] || "未知";
  };
  // 制度分类
  const getCategoryText = category => {
    const categoryMap = {
      hr: "人事制度",
      finance: "财务制度",
      safety: "安全制度",
      tech: "技术制度",
    };
    return categoryMap[category] || "未知";
  };
  // 搜索印章申请
  const searchSealApplications = () => {
    page.current = 1;
    getSealApplicationList();
  // ElMessage.success('搜索完成')
}
// 重置印章申请搜索
const resetSealSearch = () => {
  sealSearchForm.title = ''
  sealSearchForm.status = ''
  searchSealApplications()
}
// 搜索制度
const searchRegulations = () => {
  page.current=1
  getRegulationList()
}
// 重置制度搜索
const resetRegulationSearch = () => {
  regulationSearchForm.title = ''
  regulationSearchForm.category = ''
  searchRegulations()
}
// 提交用印申请
const submitSealApplication = async () => {
  try {
    await sealFormRef.value.validate()
    addSealApplication(sealForm).then(res => {
      if(res.code == 200){
        ElMessage.success('申请提交成功')
        showSealApplyDialog.value = false
        getSealApplicationList()
        Object.assign(sealForm, {
        applicationNum: '',
        title: '',
        sealType: '',
        reason: '',
        urgency: 'normal',
        status: 'pending'
    // ElMessage.success('搜索完成')
  };
  // 重置印章申请搜索
  const resetSealSearch = () => {
    sealSearchForm.title = "";
    sealSearchForm.status = "";
    searchSealApplications();
  };
  // 搜索制度
  const searchRegulations = () => {
    page.current = 1;
    getRegulationList();
  };
  // 重置制度搜索
  const resetRegulationSearch = () => {
    regulationSearchForm.title = "";
    regulationSearchForm.category = "";
    searchRegulations();
  };
  // 提交用印申请
  const submitSealApplication = async () => {
    try {
      await sealFormRef.value.validate();
      addSealApplication(sealForm)
        .then(res => {
          if (res.code == 200) {
            ElMessage.success("申请提交成功");
            showSealApplyDialog.value = false;
            getSealApplicationList();
            Object.assign(sealForm, {
              applicationNum: "",
              title: "",
              sealType: "",
              reason: "",
              urgency: "normal",
              status: "pending",
            });
          }
        })
        .catch(err => {
          ElMessage.error(err.msg);
        });
    } catch (error) {
      ElMessage.error("请完善申请信息");
    }
  };
  // 新增
  const handleAdd = () => {
    operationType.value = "add";
    resetRegulationForm();
    showRegulationDialog.value = true;
  };
  // 编辑
  const handleEdit = row => {
    operationType.value = "edit";
    Object.assign(regulationForm, row);
    showRegulationDialog.value = true;
  };
  // 废弃
  const repealEdit = row => {
    operationType.value = "edit";
    Object.assign(regulationForm, row);
    regulationForm.status = "repealed";
    ElMessageBox.confirm("确认废弃该制度?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        updateRuleManagement(regulationForm).then(res => {
          if (res.code == 200) {
            ElMessage.success("制度废弃成功");
            // showRegulationDialog.value = false
            getRegulationList();
            resetRegulationForm();
          }
        });
      })
      .catch(() => {
        ElMessage({
          type: "info",
          message: "已取消废弃",
        });
      });
  };
  // 发布制度
  const submitRegulation = async () => {
    try {
      await regulationFormRef.value.validate();
      if (operationType.value == "add") {
        addRuleManagement(regulationForm).then(res => {
          if (res.code == 200) {
            ElMessage.success("制度发布成功");
            showRegulationDialog.value = false;
            getRegulationList();
            resetRegulationForm();
          }
        });
      } else {
        updateRuleManagement(regulationForm).then(res => {
          if (res.code == 200) {
            ElMessage.success("制度编辑成功");
            showRegulationDialog.value = false;
            resetRegulationForm();
            getRegulationList();
          }
        });
      }
    }).catch(err => {
      ElMessage.error(err.msg)
    })
  } catch (error) {
    ElMessage.error('请完善申请信息')
  }
}
// 新增
const handleAdd = () => {
  operationType.value = 'add'
  resetRegulationForm()
  showRegulationDialog.value = true
}
    } catch (err) {
      ElMessage.error(err.msg);
    }
  };
  //重置制度表单
  const resetRegulationForm = () => {
    Object.assign(regulationForm, {
      id: "",
      regulationNum: "",
      title: "",
      category: "",
      content: "",
      version: "",
      status: "active",
      readCount: 0,
      effectiveTime: "",
      scope: [],
      requireConfirm: false,
    });
  };
// 编辑
const handleEdit = (row) => {
  operationType.value = 'edit'
  Object.assign(regulationForm, row)
  showRegulationDialog.value = true
}
// 废弃
const repealEdit = (row) => {
  operationType.value = 'edit'
  Object.assign(regulationForm, row)
  regulationForm.status = 'repealed'
  ElMessageBox.confirm('确认废弃该制度?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    updateRuleManagement(regulationForm).then(res => {
      if(res.code == 200){
        ElMessage.success('制度废弃成功')
        // showRegulationDialog.value = false
        getRegulationList()
        resetRegulationForm()
  // 查看用印申请详情
  const viewSealDetail = row => {
    currentSealDetail.value = row;
    showSealDetailDialog.value = true;
  };
  // 审批用印申请
  const approveSeal = row => {
    console.log(row);
    ElMessageBox.confirm("确认通过该用印申请?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      row.status = "approved";
      updateSealApplication(row).then(res => {
        if (res.code == 200) {
          ElMessage.success("审批通过");
        }
      });
    });
  };
  // 拒绝用印申请
  const rejectSeal = row => {
    ElMessageBox.prompt("请输入拒绝原因", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      inputPattern: /\S+/,
      inputErrorMessage: "拒绝原因不能为空",
    }).then(({ value }) => {
      row.status = "rejected";
      updateSealApplication(row).then(res => {
        if (res.code == 200) {
          ElMessage.success("审批拒绝");
        }
      });
      ElMessage.success("已拒绝申请");
    });
  };
  // 获取在职员工列表
  const getList = () => {
    tableLoading.value = true;
    //获取当前登录用户信息
    getUserProfile().then(res => {
      if (res.code == 200) {
        console.log(res.data.userName);
        currentUser.value = res.data.userName;
      }
    })
  }).catch(() => {
    ElMessage({
      type: 'info',
      message: '已取消废弃'
    })
  })
}
// 发布制度
const submitRegulation = async () => {
  try {
    await regulationFormRef.value.validate()
    if(operationType.value == 'add'){
      addRuleManagement(regulationForm).then(res => {
        if(res.code == 200){
          ElMessage.success('制度发布成功')
          showRegulationDialog.value = false
          getRegulationList()
          resetRegulationForm()
    });
    staffJoinListPage({ staffState: 1, ...page })
      .then(res => {
        tableLoading.value = false;
        // tableData.value = res.data.records
        // //筛选出和currentUser同名的人员
        tableData.value = res.data.records.filter(
          item => item.staffName === currentUser.value
        );
        page.total = res.data.total;
        if (tableData.value.length == 0) {
          ElMessage.error("当前用户未加入任何部门");
        }
      })
    }else{
      updateRuleManagement(regulationForm).then(res => {
        if(res.code == 200){
          ElMessage.success('制度编辑成功')
          showRegulationDialog.value = false
          resetRegulationForm()
          getRegulationList()
      }})}
  }catch(err){
    ElMessage.error(err.msg)
  }
}
//重置制度表单
const resetRegulationForm = () => {
  Object.assign(regulationForm, {
    id: '',
    regulationNum: '',
    title: '',
    category: '',
    content: '',
    version: '',
    status: 'active',
    readCount: 0,
    effectiveTime: '',
    scope: [],
    requireConfirm: false
})
}
      .catch(err => {
        tableLoading.value = false;
      });
  };
// 查看用印申请详情
const viewSealDetail = (row) => {
  currentSealDetail.value = row
  showSealDetailDialog.value = true
}
// 审批用印申请
const approveSeal = (row) => {
  console.log(row)
  ElMessageBox.confirm('确认通过该用印申请?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    row.status = 'approved'
    updateSealApplication(row).then(res => {
      if(res.code == 200){
        ElMessage.success('审批通过')
  // 查看制度版本历史
  const viewVersionHistory = row => {
    showVersionHistoryDialog.value = true;
    const params = {
      category: row.category,
    };
    listRuleManagement(page, params).then(res => {
      if (res.code == 200) {
        versionHistory.value = res.data.records;
      }
    })
  })
}
// 拒绝用印申请
const rejectSeal = (row) => {
  ElMessageBox.prompt('请输入拒绝原因', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    inputPattern: /\S+/,
    inputErrorMessage: '拒绝原因不能为空'
  }).then(({ value }) => {
    row.status = 'rejected'
    updateSealApplication(row).then(res => {
      if(res.code == 200){
        ElMessage.success('审批拒绝')
      }
    })
    ElMessage.success('已拒绝申请')
  })
}
// 获取在职员工列表
const getList = () => {
  tableLoading.value = true;
      //获取当前登录用户信息
  getUserProfile().then(res => {
    if(res.code == 200){
      console.log(res.data.userName)
      currentUser.value = res.data.userName
    }
  })
  staffJoinListPage({staffState: 1, ...page}).then(res => {
    tableLoading.value = false;
    // tableData.value = res.data.records
    // //筛选出和currentUser同名的人员
    tableData.value = res.data.records.filter(item => item.staffName === currentUser.value)
    page.total = res.data.total;
    if(tableData.value.length == 0){
    ElMessage.error('当前用户未加入任何部门')
    }
  }).catch(err => {
    tableLoading.value = false;
  })
};
// 查看制度版本历史
const viewVersionHistory = (row) => {
  showVersionHistoryDialog.value = true
  const params = {
    category: row.category
  }
  listRuleManagement(page,params).then(res => {
    if(res.code == 200){
      versionHistory.value = res.data.records
    }
  })
}
// 查看制度详情
const viewRegulation = (row) => {
  getList()
  currentRegulationDetail.value = row
  showRegulationDetailDialog.value = true
  getReadingStatusByRuleId(row.id).then(res => {
    if(res.code == 200){
      readStatusList.value = res.data
      if(readStatusList.value.length==0 && tableData.value.length>0){
    });
  };
  // 查看制度详情
  const viewRegulation = row => {
    getList();
    currentRegulationDetail.value = row;
    showRegulationDetailDialog.value = true;
    getReadingStatusByRuleId(row.id).then(res => {
      if (res.code == 200) {
        readStatusList.value = res.data;
        if (readStatusList.value.length == 0 && tableData.value.length > 0) {
          const params = {
          ruleId: row.id,
          employee: tableData.value[0].staffName,
          department: tableData.value[0].postJob,
          status: 'unconfirmed'
            ruleId: row.id,
            employee: tableData.value[0].staffName,
            department: tableData.value[0].postJob,
            status: "unconfirmed",
          };
          addReadingStatus(params).then(res => {
            if (res.code == 200) {
              ElMessage.success("制度阅读成功");
            }
          });
        }
        addReadingStatus(params).then(res => {
          if(res.code == 200){
            ElMessage.success('制度阅读成功')
          }
        })
      }
    }
  })
}
// 查看制度阅读状态
const viewReadStatus = (row) => {
  showReadStatusDialog.value = true
  //查看阅读状态列表
  getReadingStatusByRuleId(row.id).then(res => {
    if(res.code == 200){
      readStatusList.value = res.data
    }
  })
}
//确认查看
const resetForm = (row) => {
  console.log("row",row)
  row.readCount = row.readCount + 1
  updateRuleManagement(row).then(res => {
    if(res.code == 200){
      ElMessage.success('查看数量修改成功')
      //修改阅读状态
      //根据制度id和当前登录的员工得到阅读状态
      // let item = readStatusList.value.filter(item => item.employee == tableData.value[0].staffName )
      // if(item.length>0){
      //   item[0].status = 'confirmed',
      //   item[0].confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
      // }
      // 筛选当前员工对应该制度的阅读状态记录
      let statusItem = readStatusList.value.find(item => item.employee === tableData.value[0].staffName && item.ruleId === row.id);
      if (statusItem) {
        // 如果找到记录,更新状态和确认时间
        statusItem.status = 'confirmed';
        // 格式化时间为"YYYY-MM-DD HH:mm:ss"格式
        const now = new Date();
        statusItem.confirmTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
        // statusItem.confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
        updateReadingStatus(statusItem).then(res => {
          if(res.code == 200){
            ElMessage.success('制度阅读状态修改成功')
          }
        })
    });
  };
  // 查看制度阅读状态
  const viewReadStatus = row => {
    showReadStatusDialog.value = true;
    //查看阅读状态列表
    getReadingStatusByRuleId(row.id).then(res => {
      if (res.code == 200) {
        readStatusList.value = res.data;
      }
    });
  };
    }
  })
}
  //确认查看
  const resetForm = row => {
    console.log("row", row);
    row.readCount = row.readCount + 1;
// 导出用印申请
const { proxy } = getCurrentInstance()
const handleExport = () => {
  proxy.download('/sealApplicationManagement/export', { ...sealSearchForm }, '用印申请.xlsx')
}
    updateRuleManagement(row).then(res => {
      if (res.code == 200) {
        ElMessage.success("查看数量修改成功");
        //修改阅读状态
        //根据制度id和当前登录的员工得到阅读状态
        // let item = readStatusList.value.filter(item => item.employee == tableData.value[0].staffName )
        // if(item.length>0){
        //   item[0].status = 'confirmed',
        //   item[0].confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
        // }
        // 筛选当前员工对应该制度的阅读状态记录
        let statusItem = readStatusList.value.find(
          item =>
            item.employee === tableData.value[0].staffName &&
            item.ruleId === row.id
        );
// 获取印章申请列表数据
const getSealApplicationList = async () => {
  tableLoading.value = true
  listSealApplication(page,sealSearchForm)
  .then(res => {
    //获取当前登录的部门信息
// 获取当前登录的部门信息并过滤数据
    const currentFactoryName = userStore.currentFactoryName
    if (currentFactoryName) {
      // 根据currentFactoryName过滤出department相同的数据
      sealApplications.value = res.data.records.filter(item => item.department === currentFactoryName)
      // 更新过滤后的总数
      page.total = sealApplications.value.length
    } else {
      // 如果没有currentFactoryName,则显示所有数据
      sealApplications.value = res.data.records
      page.total = res.data.total
    }
    // sealApplications.value = res.data.records
    // page.value.total = res.data.total;
    tableLoading.value = false;
        if (statusItem) {
          // 如果找到记录,更新状态和确认时间
          statusItem.status = "confirmed";
          // 格式化时间为"YYYY-MM-DD HH:mm:ss"格式
          const now = new Date();
          statusItem.confirmTime = `${now.getFullYear()}-${String(
            now.getMonth() + 1
          ).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")} ${String(
            now.getHours()
          ).padStart(2, "0")}:${String(now.getMinutes()).padStart(
            2,
            "0"
          )}:${String(now.getSeconds()).padStart(2, "0")}`;
          // statusItem.confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
  }).catch(err => {
    tableLoading.value = false;
  })
}
// 获取规章制度列表数据
const getRegulationList = async () => {
  tableLoading.value = true
  listRuleManagement(page,regulationSearchForm)
  .then(res => {
          updateReadingStatus(statusItem).then(res => {
            if (res.code == 200) {
              ElMessage.success("制度阅读状态修改成功");
            }
          });
        }
      }
    });
  };
    regulations.value = res.data.records
    // 过滤掉已废弃的制度
    // regulations.value = res.data.records.filter(item => item.status !== 'repealed')
    page.total = res.data.total;
    tableLoading.value = false;
  // 导出用印申请
  const { proxy } = getCurrentInstance();
  const handleExport = () => {
    proxy.download(
      "/sealApplicationManagement/export",
      { ...sealSearchForm },
      "用印申请.xlsx"
    );
  };
  }).catch(err => {
    tableLoading.value = false;
  })
}
  // 获取印章申请列表数据
  const getSealApplicationList = async () => {
    tableLoading.value = true;
    listSealApplication(page, sealSearchForm)
      .then(res => {
        //获取当前登录的部门信息
        // 获取当前登录的部门信息并过滤数据
        const currentFactoryName = userStore.currentFactoryName;
        if (currentFactoryName) {
          // 根据currentFactoryName过滤出department相同的数据
          sealApplications.value = res.data.records.filter(
            item => item.department === currentFactoryName
          );
          // 更新过滤后的总数
          page.total = sealApplications.value.length;
        } else {
          // 如果没有currentFactoryName,则显示所有数据
          sealApplications.value = res.data.records;
          page.total = res.data.total;
        }
        // sealApplications.value = res.data.records
        // page.value.total = res.data.total;
        tableLoading.value = false;
      })
      .catch(err => {
        tableLoading.value = false;
      });
  };
  // 获取规章制度列表数据
  const getRegulationList = async () => {
    tableLoading.value = true;
    listRuleManagement(page, regulationSearchForm)
      .then(res => {
        regulations.value = res.data.records;
        // 过滤掉已废弃的制度
        // regulations.value = res.data.records.filter(item => item.status !== 'repealed')
        page.total = res.data.total;
        tableLoading.value = false;
      })
      .catch(err => {
        tableLoading.value = false;
      });
  };
onMounted(() => {
  // 初始化
  getSealApplicationList()
  getRegulationList()
})
  onMounted(() => {
    // 初始化
    getSealApplicationList();
    getRegulationList();
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
}
  .app-container {
    padding: 20px;
  }
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
.tab-content {
  padding: 20px 0;
}
  .tab-content {
    padding: 20px 0;
  }
.mb-20 {
  margin-bottom: 20px;
}
  .mb-20 {
    margin-bottom: 20px;
  }
.mt-20 {
  margin-top: 20px;
}
  .mt-20 {
    margin-top: 20px;
  }
.ml-10 {
  margin-left: 10px;
}
  .ml-10 {
    margin-left: 10px;
  }
.regulation-content {
  background-color: #f5f5f5;
  padding: 15px;
  border-radius: 4px;
  line-height: 1.6;
  white-space: pre-wrap;
  height: 200px;
}
  .regulation-content {
    background-color: #f5f5f5;
    padding: 15px;
    border-radius: 4px;
    line-height: 1.6;
    white-space: pre-wrap;
    height: 200px;
  }
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
  }
</style>
src/views/equipmentManagement/defectManagement/index.vue
@@ -2,27 +2,34 @@
  <div class="defect-management">
    <!-- 操作按钮 -->
    <div class="actions">
      <el-button type="primary" @click="showRegisterDialog = true">登记缺陷</el-button>
      <el-button type="primary"
                 @click="showRegisterDialog = true">登记缺陷</el-button>
    </div>
    <!-- 缺陷列表 -->
    <el-table :data="defectList" style="width: 100%; margin-top: 10px;" border>
      <el-table-column prop="deviceName" label="设备名称" width="180"></el-table-column>
      <el-table-column prop="defectDescription" label="缺陷描述" win-width="300"></el-table-column>
      <el-table-column prop="status" label="状态" width="220">
    <el-table :data="defectList"
              style="width: 100%; margin-top: 10px;"
              border>
      <el-table-column prop="deviceName"
                       label="设备名称"
                       width="180"></el-table-column>
      <el-table-column prop="defectDescription"
                       label="缺陷描述"
                       win-width="300"></el-table-column>
      <el-table-column prop="status"
                       label="状态"
                       width="220">
        <template #default="{ row }">
          <el-tag :type="row.status === '严重缺陷' ? 'danger' : 'success'">
            {{ row.status }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="220">
      <el-table-column label="操作"
                       width="220">
        <template #default="{ row }">
          <el-button
            v-if="row.status === '严重缺陷' || row.status === '一般缺陷'"
            type="text"
            @click="eliminateDefect(row)"
          >
          <el-button v-if="row.status === '严重缺陷' || row.status === '一般缺陷'"
                     type="text"
                     @click="eliminateDefect(row)">
            消除缺陷
          </el-button>
          <!-- <el-button
@@ -32,30 +39,38 @@
          >
            转维修单
          </el-button> -->
          <el-button type="text" @click="getLedger(row.deviceLedgerId)">
          <el-button type="text"
                     @click="getLedger(row.deviceLedgerId)">
            查看台账
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 缺陷登记对话框 -->
    <el-dialog title="登记设备缺陷" v-model="showRegisterDialog" width="50%">
      <el-form :model="defectForm" :rules="defectRules" ref="defectFormRef" label-width="100px">
        <el-form-item label="设备名称" prop="deviceName">
          <el-select v-model="defectForm.deviceLedgerId" @change="setDeviceModel">
            <el-option
              v-for="(item, index) in deviceOptions"
              :key="index"
              :label="item.deviceName"
              :value="item.id"
            ></el-option>
    <el-dialog title="登记设备缺陷"
               v-model="showRegisterDialog"
               width="50%">
      <el-form :model="defectForm"
               :rules="defectRules"
               ref="defectFormRef"
               label-width="100px">
        <el-form-item label="设备名称"
                      prop="deviceName">
          <el-select v-model="defectForm.deviceLedgerId"
                     @change="setDeviceModel">
            <el-option v-for="(item, index) in deviceOptions"
                       :key="index"
                       :label="item.deviceName"
                       :value="item.id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="缺陷描述" prop="defectDescription">
          <el-input type="textarea" v-model="defectForm.defectDescription"></el-input>
        <el-form-item label="缺陷描述"
                      prop="defectDescription">
          <el-input type="textarea"
                    v-model="defectForm.defectDescription"></el-input>
        </el-form-item>
        <el-form-item label="设备状态" prop="status">
        <el-form-item label="设备状态"
                      prop="status">
          <el-radio-group v-model="defectForm.status">
            <el-radio label="正常">正常</el-radio>
            <el-radio label="一般缺陷">一般缺陷</el-radio>
@@ -65,157 +80,168 @@
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="submitDefectForm">确定</el-button>
          <el-button @click="showRegisterDialog = false">取消</el-button>
          <el-button type="primary" @click="submitDefectForm">确定</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 缺陷设备台账对话框 -->
    <el-dialog title="缺陷设备台账" v-model="showLedgerDialog" width="80%">
      <el-table :data="ledgerList" style="width: 100%; margin-top: 10px;" border>
        <el-table-column prop="deviceName" label="设备名称"></el-table-column>
        <el-table-column prop="defectDescription" label="缺陷描述"></el-table-column>
        <el-table-column prop="status" label="状态"></el-table-column>
        <el-table-column prop="eliminateTime" label="消缺时间"></el-table-column>
    <el-dialog title="缺陷设备台账"
               v-model="showLedgerDialog"
               width="80%">
      <el-table :data="ledgerList"
                style="width: 100%; margin-top: 10px;"
                border>
        <el-table-column prop="deviceName"
                         label="设备名称"></el-table-column>
        <el-table-column prop="defectDescription"
                         label="缺陷描述"></el-table-column>
        <el-table-column prop="status"
                         label="状态"></el-table-column>
        <el-table-column prop="eliminateTime"
                         label="消缺时间"></el-table-column>
      </el-table>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus';
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
// 假设以下是后端接口
import {
  registerDefect,
  getDefectList,
  eliminateDefect as apiEliminateDefect,
  getDefectLedger,
  deleteDefect
} from '@/api/equipmentManagement/defectManagement';
  import { ref, reactive } from "vue";
  import { ElMessage } from "element-plus";
  import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
  // 假设以下是后端接口
  import {
    registerDefect,
    getDefectList,
    eliminateDefect as apiEliminateDefect,
    getDefectLedger,
    deleteDefect,
  } from "@/api/equipmentManagement/defectManagement";
// 缺陷列表
const defectList = ref([]);
// 登记对话框显示状态
const showRegisterDialog = ref(false);
// 台账对话框显示状态
const showLedgerDialog = ref(false);
// 缺陷表单
const defectForm = reactive({
  deviceLedgerId: '',
  defectDescription: '',
  status: '',
});
const deviceOptions = ref([]);
// 表单验证规则
const defectRules = reactive({
  deviceLedgerId: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
  defectDescription: [{ required: true, message: '请输入缺陷描述', trigger: 'blur' }]
});
// 表单引用
const defectFormRef = ref(null);
// 台账列表
const ledgerList = ref([]);
  // 缺陷列表
  const defectList = ref([]);
  // 登记对话框显示状态
  const showRegisterDialog = ref(false);
  // 台账对话框显示状态
  const showLedgerDialog = ref(false);
  // 缺陷表单
  const defectForm = reactive({
    deviceLedgerId: "",
    defectDescription: "",
    status: "",
  });
  const deviceOptions = ref([]);
  // 表单验证规则
  const defectRules = reactive({
    deviceLedgerId: [
      { required: true, message: "请输入设备名称", trigger: "blur" },
    ],
    defectDescription: [
      { required: true, message: "请输入缺陷描述", trigger: "blur" },
    ],
  });
  // 表单引用
  const defectFormRef = ref(null);
  // 台账列表
  const ledgerList = ref([]);
const loadDeviceName = async () => {
  const { data } = await getDeviceLedger();
  // console.log(data);
  deviceOptions.value = data;
};
  const loadDeviceName = async () => {
    const { data } = await getDeviceLedger();
    // console.log(data);
    deviceOptions.value = data;
  };
// 获取缺陷列表
const fetchDefectList = async () => {
  try {
    const res = await getDefectList();
    if (res.code === 200) {
      defectList.value = res.data.records;
    } else {
      ElMessage.error(res.message || '获取缺陷列表失败');
  // 获取缺陷列表
  const fetchDefectList = async () => {
    try {
      const res = await getDefectList();
      if (res.code === 200) {
        defectList.value = res.data.records;
      } else {
        ElMessage.error(res.message || "获取缺陷列表失败");
      }
    } catch (error) {
      ElMessage.error("获取缺陷列表失败");
    }
  } catch (error) {
    ElMessage.error('获取缺陷列表失败');
  }
};
  };
// 提交缺陷登记表单
const submitDefectForm = async () => {
  if (!defectFormRef.value) return;
  try {
    await defectFormRef.value.validate();
    const res = await registerDefect(defectForm);
    if (res.code === 200) {
      ElMessage.success('缺陷登记成功');
      showRegisterDialog.value = false;
      fetchDefectList();
    } else {
      ElMessage.error(res.message || '缺陷登记失败');
  // 提交缺陷登记表单
  const submitDefectForm = async () => {
    if (!defectFormRef.value) return;
    try {
      await defectFormRef.value.validate();
      const res = await registerDefect(defectForm);
      if (res.code === 200) {
        ElMessage.success("缺陷登记成功");
        showRegisterDialog.value = false;
        fetchDefectList();
      } else {
        ElMessage.error(res.message || "缺陷登记失败");
      }
    } catch (error) {
      ElMessage.error("请填写完整表单信息");
    }
  } catch (error) {
    ElMessage.error('请填写完整表单信息');
  }
};
  };
// 消除缺陷
const eliminateDefect = async (row) => {
  try {
    const res = await apiEliminateDefect(row);
    if (res.code === 200) {
      ElMessage.success('缺陷消除成功');
      fetchDefectList();
    } else {
      ElMessage.error(res.message || '缺陷消除失败');
  // 消除缺陷
  const eliminateDefect = async row => {
    try {
      const res = await apiEliminateDefect(row);
      if (res.code === 200) {
        ElMessage.success("缺陷消除成功");
        fetchDefectList();
      } else {
        ElMessage.error(res.message || "缺陷消除失败");
      }
    } catch (error) {
      ElMessage.error("缺陷消除失败");
    }
  } catch (error) {
    ElMessage.error('缺陷消除失败');
  }
};
  };
// // 转维修工单
// const transferToRepairOrder = async (id) => {
//   try {
//     const res = await transferToRepair(id);
//     if (res.code === 200) {
//       ElMessage.success('转维修工单成功');
//     } else {
//       ElMessage.error(res.message || '转维修工单失败');
//     }
//   } catch (error) {
//     ElMessage.error('转维修工单失败');
//   }
// };
  // // 转维修工单
  // const transferToRepairOrder = async (id) => {
  //   try {
  //     const res = await transferToRepair(id);
  //     if (res.code === 200) {
  //       ElMessage.success('转维修工单成功');
  //     } else {
  //       ElMessage.error(res.message || '转维修工单失败');
  //     }
  //   } catch (error) {
  //     ElMessage.error('转维修工单失败');
  //   }
  // };
// 获取缺陷设备台账
const getLedger = async (deviceLedgerId) => {
  try {
    const res = await getDefectLedger(deviceLedgerId);
    if (res.code === 200) {
      ledgerList.value = res.data.records;
      showLedgerDialog.value = true;
    } else {
      ElMessage.error(res.message || '获取缺陷设备台账失败');
  // 获取缺陷设备台账
  const getLedger = async deviceLedgerId => {
    try {
      const res = await getDefectLedger(deviceLedgerId);
      if (res.code === 200) {
        ledgerList.value = res.data.records;
        showLedgerDialog.value = true;
      } else {
        ElMessage.error(res.message || "获取缺陷设备台账失败");
      }
    } catch (error) {
      ElMessage.error("获取缺陷设备台账失败");
    }
  } catch (error) {
    ElMessage.error('获取缺陷设备台账失败');
  }
};
  };
// 组件挂载时获取缺陷列表
const onMounted = () => {
  fetchDefectList();
  loadDeviceName();
};
onMounted();
  // 组件挂载时获取缺陷列表
  const onMounted = () => {
    fetchDefectList();
    loadDeviceName();
  };
  onMounted();
</script>
<style scoped>
.defect-management {
  padding: 20px;
}
  .defect-management {
    padding: 20px;
  }
.actions {
  margin-bottom: 10px;
}
  .actions {
    margin-bottom: 10px;
  }
</style>
src/views/equipmentManagement/inspectionManagement/components/formDia.vue
@@ -1,247 +1,288 @@
<template>
    <div>
        <el-dialog :title="operationType === 'add' ? '新增巡检任务' : '编辑巡检任务'"
                             v-model="dialogVisitable" width="800px" @close="cancel">
            <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="设备名称" prop="taskId">
                            <el-select v-model="form.taskId" @change="setDeviceModel" filterable>
                                <el-option
                                    v-for="(item, index) in deviceOptions"
                                    :key="index"
                                    :label="item.deviceName"
                                    :value="item.id"
                                ></el-option>
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="巡检人" prop="inspector">
                            <el-select v-model="form.inspector"                 filterable
                                                 default-first-option
                                                 :reserve-keyword="false" placeholder="请选择" multiple clearable>
                                <el-option v-for="item in userList" :label="item.nickName" :value="item.userId" :key="item.userId"/>
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="备注" prop="remarks">
                            <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="登记时间" prop="dateStr">
                            <el-date-picker
                                v-model="form.dateStr"
                                type="date"
                                placeholder="选择登记日期"
                                format="YYYY-MM-DD"
                                value-format="YYYY-MM-DD"
                                style="width: 100%"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="任务频率" prop="frequencyType">
                            <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                                <el-option label="每日" value="DAILY"/>
                                <el-option label="每周" value="WEEKLY"/>
                                <el-option label="每月" value="MONTHLY"/>
                                <!-- <el-option label="季度" value="QUARTERLY"/> -->
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%">
                                <el-option label="周一" value="MON"/>
                                <el-option label="周二" value="TUE"/>
                                <el-option label="周三" value="WED"/>
                                <el-option label="周四" value="THU"/>
                                <el-option label="周五" value="FRI"/>
                                <el-option label="周六" value="SAT"/>
                                <el-option label="周日" value="SUN"/>
                            </el-select>
                            <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm"  style="width: 50%"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="DD,HH:mm"
                                value-format="DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="MM,DD,HH:mm"
                                value-format="MM,DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button @click="cancel">取消</el-button>
                    <el-button type="primary" @click="submitForm">保存</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
  <div>
    <el-dialog :title="operationType === 'add' ? '新增巡检任务' : '编辑巡检任务'"
               v-model="dialogVisitable"
               width="800px"
               @close="cancel">
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="120px">
        <el-row>
          <el-col :span="12">
            <el-form-item label="设备名称"
                          prop="taskId">
              <el-select v-model="form.taskId"
                         @change="setDeviceModel"
                         filterable>
                <el-option v-for="(item, index) in deviceOptions"
                           :key="index"
                           :label="item.deviceName"
                           :value="item.id"></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="巡检人"
                          prop="inspector">
              <el-select v-model="form.inspector"
                         filterable
                         default-first-option
                         :reserve-keyword="false"
                         placeholder="请选择"
                         multiple
                         clearable>
                <el-option v-for="item in userList"
                           :label="item.nickName"
                           :value="item.userId"
                           :key="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="备注"
                          prop="remarks">
              <el-input v-model="form.remarks"
                        placeholder="请输入备注"
                        type="textarea" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="登记时间"
                          prop="dateStr">
              <el-date-picker v-model="form.dateStr"
                              type="date"
                              placeholder="选择登记日期"
                              format="YYYY-MM-DD"
                              value-format="YYYY-MM-DD"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="任务频率"
                          prop="frequencyType">
              <el-select v-model="form.frequencyType"
                         placeholder="请选择"
                         clearable>
                <el-option label="每日"
                           value="DAILY" />
                <el-option label="每周"
                           value="WEEKLY" />
                <el-option label="每月"
                           value="MONTHLY" />
                <!-- <el-option label="季度" value="QUARTERLY"/> -->
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12"
                  v-if="form.frequencyType === 'DAILY' && form.frequencyType">
            <el-form-item label="日期"
                          prop="frequencyDetail">
              <el-time-picker v-model="form.frequencyDetail"
                              placeholder="选择时间"
                              format="HH:mm"
                              value-format="HH:mm" />
            </el-form-item>
          </el-col>
          <el-col :span="12"
                  v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
            <el-form-item label="日期"
                          prop="frequencyDetail">
              <el-select v-model="form.week"
                         placeholder="请选择"
                         clearable
                         style="width: 50%">
                <el-option label="周一"
                           value="MON" />
                <el-option label="周二"
                           value="TUE" />
                <el-option label="周三"
                           value="WED" />
                <el-option label="周四"
                           value="THU" />
                <el-option label="周五"
                           value="FRI" />
                <el-option label="周六"
                           value="SAT" />
                <el-option label="周日"
                           value="SUN" />
              </el-select>
              <el-time-picker v-model="form.time"
                              placeholder="选择时间"
                              format="HH:mm"
                              value-format="HH:mm"
                              style="width: 50%" />
            </el-form-item>
          </el-col>
          <el-col :span="12"
                  v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
            <el-form-item label="日期"
                          prop="frequencyDetail">
              <el-date-picker v-model="form.frequencyDetail"
                              type="datetime"
                              clearable
                              placeholder="选择开始日期"
                              format="DD,HH:mm"
                              value-format="DD,HH:mm" />
            </el-form-item>
          </el-col>
          <el-col :span="12"
                  v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
            <el-form-item label="日期"
                          prop="frequencyDetail">
              <el-date-picker v-model="form.frequencyDetail"
                              type="datetime"
                              clearable
                              placeholder="选择开始日期"
                              format="MM,DD,HH:mm"
                              value-format="MM,DD,HH:mm" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitForm">保存</el-button>
          <el-button @click="cancel">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {reactive, ref} from "vue";
import useUserStore from '@/store/modules/user'
import {addOrEditTimingTask} from "@/api/inspectionManagement/index.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
  import { reactive, ref } from "vue";
  import useUserStore from "@/store/modules/user";
  import { addOrEditTimingTask } from "@/api/inspectionManagement/index.js";
  import { userListNoPageByTenantId } from "@/api/system/user.js";
  import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
const { proxy } = getCurrentInstance()
const emit = defineEmits()
const userStore = useUserStore()
const dialogVisitable = ref(false);
const operationType = ref('add');
const deviceOptions = ref([]);
const data = reactive({
    form: {
        taskId: undefined,
        taskName: undefined,
        inspector: '',
        inspectorIds: '',
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: '',
        dateStr: ''
    },
    rules: {
        taskId: [{ required: true, message: "请选择设备", trigger: "change" },],
        inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" },],
        dateStr: [{ required: true, message: "请选择登记时间", trigger: "change" }]
    }
})
const { form, rules } = toRefs(data)
const userList = ref([])
  const { proxy } = getCurrentInstance();
  const emit = defineEmits();
  const userStore = useUserStore();
  const dialogVisitable = ref(false);
  const operationType = ref("add");
  const deviceOptions = ref([]);
  const data = reactive({
    form: {
      taskId: undefined,
      taskName: undefined,
      inspector: "",
      inspectorIds: "",
      remarks: "",
      frequencyType: "",
      frequencyDetail: "",
      week: "",
      time: "",
      dateStr: "",
    },
    rules: {
      taskId: [{ required: true, message: "请选择设备", trigger: "change" }],
      inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" }],
      dateStr: [{ required: true, message: "请选择登记时间", trigger: "change" }],
    },
  });
  const { form, rules } = toRefs(data);
  const userList = ref([]);
const loadDeviceName = async () => {
    const { data } = await getDeviceLedger();
    deviceOptions.value = data;
};
  const loadDeviceName = async () => {
    const { data } = await getDeviceLedger();
    deviceOptions.value = data;
  };
const setDeviceModel = (id) => {
    const option = deviceOptions.value.find((item) => item.id === id);
    if (option) {
        form.value.taskName = option.deviceName;
    }
}
  const setDeviceModel = id => {
    const option = deviceOptions.value.find(item => item.id === id);
    if (option) {
      form.value.taskName = option.deviceName;
    }
  };
// 打开弹框
const openDialog = async (type, row) => {
    dialogVisitable.value = true
    operationType.value = type
    // 重置表单
    resetForm();
    // 加载用户列表
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
    });
    // 加载设备列表
    await loadDeviceName();
    if (type === 'edit' && row) {
        form.value = {...row}
        form.value.inspector = form.value.inspectorIds.split(',').map(Number)
        // 如果有设备ID,自动设置设备信息
        if (form.value.taskId) {
            setDeviceModel(form.value.taskId);
        }
    }
}
  // 打开弹框
  const openDialog = async (type, row) => {
    dialogVisitable.value = true;
    operationType.value = type;
// 关闭对话框
const cancel = () => {
    resetForm()
    dialogVisitable.value = false
    emit('closeDia')
}
    // 重置表单
    resetForm();
// 重置表单函数
const resetForm = () => {
    if (proxy.$refs.formRef) {
        proxy.$refs.formRef.resetFields()
    }
    // 重置表单数据确保设备信息正确重置
    form.value = {
        taskId: undefined,
        taskName: undefined,
        inspector: '',
        inspectorIds: '',
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: ''
    }
}
    // 加载用户列表
    userListNoPageByTenantId().then(res => {
      userList.value = res.data;
    });
// 提交表单
const submitForm = () => {
    proxy.$refs["formRef"].validate(async valid => {
        if (valid) {
            try {
                form.value.inspectorIds = form.value.inspector.join(',')
                delete form.value.inspector
                if (form.value.frequencyType === 'WEEKLY') {
                    let frequencyDetail = ''
                    frequencyDetail = form.value.week + ',' + form.value.time
                    form.value.frequencyDetail = frequencyDetail
                }
                let res = await userStore.getInfo()
                form.value.registrantId = res.user.userId
                await addOrEditTimingTask(form.value)
                cancel()
                proxy.$modal.msgSuccess('提交成功')
            } catch (error) {
                proxy.$modal.msgError('提交失败,请重试')
            }
        }
    })
}
defineExpose({ openDialog })
    // 加载设备列表
    await loadDeviceName();
    if (type === "edit" && row) {
      form.value = { ...row };
      form.value.inspector = form.value.inspectorIds.split(",").map(Number);
      // 如果有设备ID,自动设置设备信息
      if (form.value.taskId) {
        setDeviceModel(form.value.taskId);
      }
    }
  };
  // 关闭对话框
  const cancel = () => {
    resetForm();
    dialogVisitable.value = false;
    emit("closeDia");
  };
  // 重置表单函数
  const resetForm = () => {
    if (proxy.$refs.formRef) {
      proxy.$refs.formRef.resetFields();
    }
    // 重置表单数据确保设备信息正确重置
    form.value = {
      taskId: undefined,
      taskName: undefined,
      inspector: "",
      inspectorIds: "",
      remarks: "",
      frequencyType: "",
      frequencyDetail: "",
      week: "",
      time: "",
    };
  };
  // 提交表单
  const submitForm = () => {
    proxy.$refs["formRef"].validate(async valid => {
      if (valid) {
        try {
          form.value.inspectorIds = form.value.inspector.join(",");
          delete form.value.inspector;
          if (form.value.frequencyType === "WEEKLY") {
            let frequencyDetail = "";
            frequencyDetail = form.value.week + "," + form.value.time;
            form.value.frequencyDetail = frequencyDetail;
          }
          let res = await userStore.getInfo();
          form.value.registrantId = res.user.userId;
          await addOrEditTimingTask(form.value);
          cancel();
          proxy.$modal.msgSuccess("提交成功");
        } catch (error) {
          proxy.$modal.msgError("提交失败,请重试");
        }
      }
    });
  };
  defineExpose({ openDialog });
</script>
<style scoped>
</style>
src/views/equipmentManagement/spareParts/index.vue
@@ -2,91 +2,114 @@
  <div class="spare-part-category">
    <div class="table_list">
      <div class="actions">
        <el-text class="mx-1" size="large">设备分类</el-text>
        <el-text class="mx-1"
                 size="large">设备分类</el-text>
        <div>
          <el-button @click="fetchTreeData" :loading="loading">刷新</el-button>
          <el-button type="primary" @click="addCategory" >新增</el-button>
          <el-button @click="fetchTreeData"
                     :loading="loading">刷新</el-button>
          <el-button type="primary"
                     @click="addCategory">新增</el-button>
        </div>
      </div>
      <el-table
        v-loading="loading"
        :data="renderTableData"
        style="width: 100%; margin-top: 10px;"
        border
        row-key="id"
        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
      >
        <el-table-column prop="name" label="分类名称" width="450">
      <el-table v-loading="loading"
                :data="renderTableData"
                style="width: 100%; margin-top: 10px;"
                border
                row-key="id"
                :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
        <el-table-column prop="name"
                         label="分类名称"
                         width="450">
          <template #default="{ row }">
            <span :style="{ paddingLeft: getIndentation(row) + 'px' }">
              {{ row.name }}
            </span>
          </template>
        </el-table-column>
        <el-table-column prop="sparePartsNo" label="分类编号" width="200"></el-table-column>
        <el-table-column prop="status" label="状态" width="100">
        <el-table-column prop="sparePartsNo"
                         label="分类编号"
                         width="200"></el-table-column>
        <el-table-column prop="status"
                         label="状态"
                         width="100">
          <template #default="{ row }">
            <el-tag type="success" size="small">{{ row.status }}</el-tag>
            <el-tag type="success"
                    size="small">{{ row.status }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="description" label="描述" win-width="330"></el-table-column>
        <el-table-column label="操作" width="180" fixed="right">
        <el-table-column prop="description"
                         label="描述"
                         win-width="330"></el-table-column>
        <el-table-column label="操作"
                         width="180"
                         fixed="right">
          <template #default="{ row }">
            <el-button
              type="text"
              size="small"
              @click="() => editCategory(row)"
              :disabled="loading"
            >
            <el-button type="text"
                       size="small"
                       @click="() => editCategory(row)"
                       :disabled="loading">
              编辑
            </el-button>
            <el-button
              type="text"
              size="small"
              @click="() => deleteCategory(row.id)"
              style="color: #f56c6c;"
              :disabled="loading"
            >
            <el-button type="text"
                       size="small"
                       @click="() => deleteCategory(row.id)"
                       style="color: #f56c6c;"
                       :disabled="loading">
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <el-dialog title="分类管理" v-model="dialogVisible" width="60%">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-form-item label="分类名称" prop="name">
    <el-dialog title="分类管理"
               v-model="dialogVisible"
               width="60%">
      <el-form :model="form"
               :rules="rules"
               ref="formRef"
               label-width="100px">
        <el-form-item label="分类名称"
                      prop="name">
          <el-input v-model="form.name"></el-input>
        </el-form-item>
        <el-form-item label="分类编号" prop="sparePartsNo">
        <el-form-item label="分类编号"
                      prop="sparePartsNo">
          <el-input v-model="form.sparePartsNo"></el-input>
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-select v-model="form.status" placeholder="请选择状态">
            <el-option label="正常" value="正常"></el-option>
            <el-option label="禁用" value="禁用"></el-option>
        <el-form-item label="状态"
                      prop="status">
          <el-select v-model="form.status"
                     placeholder="请选择状态">
            <el-option label="正常"
                       value="正常"></el-option>
            <el-option label="禁用"
                       value="禁用"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="描述" prop="description">
        <el-form-item label="描述"
                      prop="description">
          <el-input v-model="form.description"></el-input>
        </el-form-item>
        <el-form-item label="上级分类" prop="parentId">
          <el-select v-model="form.parentId" placeholder="请选择上级分类">
            <el-option label="无上级分类" :value="null"></el-option>
            <el-option
              v-for="(item, index) in categories"
              :key="index"
              :label="item.name"
              :value="item.id"
            ></el-option>
        <el-form-item label="上级分类"
                      prop="parentId">
          <el-select v-model="form.parentId"
                     placeholder="请选择上级分类">
            <el-option label="无上级分类"
                       :value="null"></el-option>
            <el-option v-for="(item, index) in categories"
                       :key="index"
                       :label="item.name"
                       :value="item.id"></el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false" :disabled="formLoading">取消</el-button>
          <el-button type="primary" @click="submitForm" :loading="formLoading">确定</el-button>
          <el-button type="primary"
                     @click="submitForm"
                     :loading="formLoading">确定</el-button>
          <el-button @click="dialogVisible = false"
                     :disabled="formLoading">取消</el-button>
        </span>
      </template>
    </el-dialog>
@@ -94,324 +117,326 @@
</template>
<script setup>
import { ref, computed, onMounted, reactive, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getSparePartsList, addSparePart, editSparePart, delSparePart,getSparePartsTree } from "@/api/equipmentManagement/spareParts";
  import { ref, computed, onMounted, reactive, watch } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    getSparePartsList,
    addSparePart,
    editSparePart,
    delSparePart,
    getSparePartsTree,
  } from "@/api/equipmentManagement/spareParts";
// 加载状态
const loading = ref(false);
const formLoading = ref(false);
// 对话框显示状态
const dialogVisible = ref(false);
// 编辑 ID
const editId = ref(null);
// 表格数据
const categories = ref([]);
// 渲染用的表格数据
// const renderTableData = computed(() => buildTree(categories.value));
const renderTableData = ref([]);
const operationType = ref('add')
// 表单引用
const formRef = ref(null);
// 表单数据
const form = reactive({
  id:'',
  name: '',
  sparePartsNo: '',
  status: '',
  description: '',
  parentId: null
});
// 表单验证规则
const rules = reactive({
  name: [
    { required: true, message: '请输入分类名称', trigger: 'blur' }
  ],
  sparePartsNo: [
    { required: true, message: '请输入分类编号', trigger: 'blur' }
  ],
  status: [
    { required: true, message: '请选择状态', trigger: 'change' }
  ]
});
// 获取缩进量
const getIndentation = (row) => {
  // 这里简单返回 20,可根据实际需求实现层级缩进逻辑
  return 20;
};
// 定义 buildTree 函数
const buildTree = (flatData) => {
  const map = {};
  const result = [];
  if(flatData){
    return result;
  }
  flatData.forEach(item => {
    map[item.id] = { ...item, children: [] };
  // 加载状态
  const loading = ref(false);
  const formLoading = ref(false);
  // 对话框显示状态
  const dialogVisible = ref(false);
  // 编辑 ID
  const editId = ref(null);
  // 表格数据
  const categories = ref([]);
  // 渲染用的表格数据
  // const renderTableData = computed(() => buildTree(categories.value));
  const renderTableData = ref([]);
  const operationType = ref("add");
  // 表单引用
  const formRef = ref(null);
  // 表单数据
  const form = reactive({
    id: "",
    name: "",
    sparePartsNo: "",
    status: "",
    description: "",
    parentId: null,
  });
  flatData.forEach(item => {
    if (item.parentId === null || !map[item.parentId]) {
      result.push(map[item.id]);
    } else {
      map[item.parentId].children.push(map[item.id]);
    }
  // 表单验证规则
  const rules = reactive({
    name: [{ required: true, message: "请输入分类名称", trigger: "blur" }],
    sparePartsNo: [
      { required: true, message: "请输入分类编号", trigger: "blur" },
    ],
    status: [{ required: true, message: "请选择状态", trigger: "change" }],
  });
  return result;
};
//获取树形结构
const fetchTreeData = async () => {
  fetchCategories();
  try {
    const res = await getSparePartsTree();
    if (res.code === 200) {
      renderTableData.value = res.data;
    } else {
      ElMessage.error(res.message || '获取分类列表失败');
  // 获取缩进量
  const getIndentation = row => {
    // 这里简单返回 20,可根据实际需求实现层级缩进逻辑
    return 20;
  };
  // 定义 buildTree 函数
  const buildTree = flatData => {
    const map = {};
    const result = [];
    if (flatData) {
      return result;
    }
  }catch (error) {
    ElMessage.error('获取分类列表失败');
  }
}
// 获取分类列表
const fetchCategories = async () => {
  loading.value = true;
  try {
    const res = await getSparePartsList();
    if (res.code === 200) {
      categories.value = res.data.records;
    } else {
      ElMessage.error(res.message || '获取分类列表失败');
    }
  } catch (error) {
    ElMessage.error('获取分类列表失败');
  } finally {
    loading.value = false;
  }
};
// 新增分类
const addCategory = () => {
  form.id = '';
  form.name = '';
  form.sparePartsNo = '';
  form.status = '';
  form.description = '';
  form.parentId = null;
  operationType.value = 'add'
  dialogVisible.value = true;
  console.log('dialogVisible 更新为', dialogVisible.value);
};
// 编辑分类
const editCategory = (row) => {
  Object.assign(form, row);
  operationType.value = 'edit'
  dialogVisible.value = true;
};
// 删除分类
const deleteCategory = async (id) => {
  try {
    await ElMessageBox.confirm('此操作将永久删除该分类,是否继续?', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    flatData.forEach(item => {
      map[item.id] = { ...item, children: [] };
    });
    loading.value = true;
    const res = await delSparePart(id);
    if (res.code === 200) {
      ElMessage.success('删除成功');
      fetchTreeData();
    } else {
      ElMessage.error(res.message || '删除失败');
    }
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('删除失败');
    }
  } finally {
    loading.value = false;
  }
};
// 提交表单
const submitForm = async () => {
  if (!formRef.value) return;
  try {
    await formRef.value.validate();
    formLoading.value = true;
    if (operationType.value === 'edit') {
      let res = await editSparePart(form);
      if (res.code === 200) {
      ElMessage.success('编辑成功');
      dialogVisible.value = false;
      fetchTreeData();
    }
    } else {
      let res = await addSparePart(form);
        if (res.code === 200) {
        ElMessage.success('编辑成功');
        dialogVisible.value = false;
        fetchTreeData();
    flatData.forEach(item => {
      if (item.parentId === null || !map[item.parentId]) {
        result.push(map[item.id]);
      } else {
        map[item.parentId].children.push(map[item.id]);
      }
    });
    return result;
  };
  //获取树形结构
  const fetchTreeData = async () => {
    fetchCategories();
    try {
      const res = await getSparePartsTree();
      if (res.code === 200) {
        renderTableData.value = res.data;
      } else {
        ElMessage.error(res.message || "获取分类列表失败");
      }
    } catch (error) {
      ElMessage.error("获取分类列表失败");
    }
  } catch (error) {
    ElMessage.error('请填写完整表单信息');
  } finally {
    formLoading.value = false;
  }
};
  };
// 组件挂载时获取分类列表
onMounted(() => {
  fetchCategories();
  fetchTreeData();
});
  // 获取分类列表
  const fetchCategories = async () => {
    loading.value = true;
    try {
      const res = await getSparePartsList();
      if (res.code === 200) {
        categories.value = res.data.records;
      } else {
        ElMessage.error(res.message || "获取分类列表失败");
      }
    } catch (error) {
      ElMessage.error("获取分类列表失败");
    } finally {
      loading.value = false;
    }
  };
  // 新增分类
  const addCategory = () => {
    form.id = "";
    form.name = "";
    form.sparePartsNo = "";
    form.status = "";
    form.description = "";
    form.parentId = null;
    operationType.value = "add";
    dialogVisible.value = true;
    console.log("dialogVisible 更新为", dialogVisible.value);
  };
  // 编辑分类
  const editCategory = row => {
    Object.assign(form, row);
    operationType.value = "edit";
    dialogVisible.value = true;
  };
  // 删除分类
  const deleteCategory = async id => {
    try {
      await ElMessageBox.confirm("此操作将永久删除该分类,是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      });
      loading.value = true;
      const res = await delSparePart(id);
      if (res.code === 200) {
        ElMessage.success("删除成功");
        fetchTreeData();
      } else {
        ElMessage.error(res.message || "删除失败");
      }
    } catch (error) {
      if (error !== "cancel") {
        ElMessage.error("删除失败");
      }
    } finally {
      loading.value = false;
    }
  };
  // 提交表单
  const submitForm = async () => {
    if (!formRef.value) return;
    try {
      await formRef.value.validate();
      formLoading.value = true;
      if (operationType.value === "edit") {
        let res = await editSparePart(form);
        if (res.code === 200) {
          ElMessage.success("编辑成功");
          dialogVisible.value = false;
          fetchTreeData();
        }
      } else {
        let res = await addSparePart(form);
        if (res.code === 200) {
          ElMessage.success("编辑成功");
          dialogVisible.value = false;
          fetchTreeData();
        }
      }
    } catch (error) {
      ElMessage.error("请填写完整表单信息");
    } finally {
      formLoading.value = false;
    }
  };
  // 组件挂载时获取分类列表
  onMounted(() => {
    fetchCategories();
    fetchTreeData();
  });
</script>
<style scoped>
.spare-part-category {
  padding: 20px;
}
.table_list {
  margin-top: unset;
}
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
  align-items: center;
}
  .spare-part-category {
    padding: 20px;
  }
  .table_list {
    margin-top: unset;
  }
  .actions {
    display: flex;
    justify-content: space-between;
    margin-bottom: 10px;
    align-items: center;
  }
/* 嵌套树形结构样式 */
.nested-tree-node {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0 4px;
  height: 30px;
  line-height: 30px;
}
  /* 嵌套树形结构样式 */
  .nested-tree-node {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 0 4px;
    height: 30px;
    line-height: 30px;
  }
.category-code {
  color: #606266;
  font-size: 12px;
  margin-left: 8px;
}
  .category-code {
    color: #606266;
    font-size: 12px;
    margin-left: 8px;
  }
.tree-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
  .tree-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
  }
/* 表格样式调整 */
.el-table {
  font-size: 14px;
}
  /* 表格样式调整 */
  .el-table {
    font-size: 14px;
  }
.el-table__header-wrapper th {
  background-color: #f5f7fa;
  font-weight: 600;
}
  .el-table__header-wrapper th {
    background-color: #f5f7fa;
    font-weight: 600;
  }
.el-table__row:hover > td {
  background-color: #fafafa;
}
  .el-table__row:hover > td {
    background-color: #fafafa;
  }
/* 嵌套树形结构特定样式 */
.nested-tree {
  background-color: transparent;
}
  /* 嵌套树形结构特定样式 */
  .nested-tree {
    background-color: transparent;
  }
.nested-tree .el-tree-node__content {
  height: auto;
  background-color: transparent;
}
  .nested-tree .el-tree-node__content {
    height: auto;
    background-color: transparent;
  }
/* 搜索框样式调整 */
.actions .el-input {
  margin-right: 10px;
  width: 200px;
}
  /* 搜索框样式调整 */
  .actions .el-input {
    margin-right: 10px;
    width: 200px;
  }
/* 按钮组样式 */
.actions > div {
  display: flex;
  gap: 10px;
}
  /* 按钮组样式 */
  .actions > div {
    display: flex;
    gap: 10px;
  }
/* 确保表格中的操作按钮不会被截断 */
.el-table-column--fixed-right .el-button {
  margin: 0 2px;
}
  /* 确保表格中的操作按钮不会被截断 */
  .el-table-column--fixed-right .el-button {
    margin: 0 2px;
  }
/* 树形节点内容样式 */
.nested-tree .el-tree-node__expand-icon {
  font-size: 12px;
  margin-right: 4px;
}
  /* 树形节点内容样式 */
  .nested-tree .el-tree-node__expand-icon {
    font-size: 12px;
    margin-right: 4px;
  }
/* 空状态样式 */
.el-table .cell:empty::before {
  content: '-';
  color: #c0c4cc;
}
  /* 空状态样式 */
  .el-table .cell:empty::before {
    content: "-";
    color: #c0c4cc;
  }
/* 展开/折叠功能样式 */
.expand-icon {
  display: inline-block;
  width: 20px;
  height: 20px;
  text-align: center;
  line-height: 20px;
  cursor: pointer;
  font-size: 12px;
  color: #909399;
}
  /* 展开/折叠功能样式 */
  .expand-icon {
    display: inline-block;
    width: 20px;
    height: 20px;
    text-align: center;
    line-height: 20px;
    cursor: pointer;
    font-size: 12px;
    color: #909399;
  }
.expand-icon.expanded {
  color: #409eff;
}
  .expand-icon.expanded {
    color: #409eff;
  }
/* 展开内容样式 */
.expand-content {
  padding: 10px 20px;
}
  /* 展开内容样式 */
  .expand-content {
    padding: 10px 20px;
  }
/* 子级内容样式 */
.child-content {
  padding-left: 40px;
}
  /* 子级内容样式 */
  .child-content {
    padding-left: 40px;
  }
/* 无子分类提示样式 */
.no-children {
  padding: 10px 20px;
  color: #909399;
  font-size: 14px;
}
  /* 无子分类提示样式 */
  .no-children {
    padding: 10px 20px;
    color: #909399;
    font-size: 14px;
  }
/* 确保展开的表格样式正确 */
.el-table .el-table__expanded-cell {
  background-color: #fafafa;
}
  /* 确保展开的表格样式正确 */
  .el-table .el-table__expanded-cell {
    background-color: #fafafa;
  }
/* 展开的子表格样式 */
.el-table .el-table {
  border-top: none;
  border-bottom: none;
}
  /* 展开的子表格样式 */
  .el-table .el-table {
    border-top: none;
    border-bottom: none;
  }
/* 展开的子表格单元格样式 */
.expand-content .el-table__body-wrapper .el-table__row {
  background-color: #ffffff;
}
  /* 展开的子表格单元格样式 */
  .expand-content .el-table__body-wrapper .el-table__row {
    background-color: #ffffff;
  }
.expand-content .el-table__body-wrapper .el-table__row:hover > td {
  background-color: #fafafa;
}
  .expand-content .el-table__body-wrapper .el-table__row:hover > td {
    background-color: #fafafa;
  }
</style>
src/views/example/DynamicTableExample.vue
@@ -1,101 +1,91 @@
<template>
  <div class="app-container">
    <div class="search-form">
      <el-form :inline="true" :model="searchForm">
      <el-form :inline="true"
               :model="searchForm">
        <el-form-item label="部门">
          <el-input
            v-model="searchForm.department"
            placeholder="请输入部门名称"
            clearable
            style="width: 200px"
          />
          <el-input v-model="searchForm.department"
                    placeholder="请输入部门名称"
                    clearable
                    style="width: 200px" />
        </el-form-item>
        <el-form-item label="姓名">
          <el-input
            v-model="searchForm.name"
            placeholder="请输入姓名"
            clearable
            style="width: 200px"
          />
          <el-input v-model="searchForm.name"
                    placeholder="请输入姓名"
                    clearable
                    style="width: 200px" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleSearch">搜索</el-button>
          <el-button type="primary"
                     @click="handleSearch">搜索</el-button>
          <el-button @click="handleReset">重置</el-button>
          <el-button type="success" @click="handleAdd">新增</el-button>
          <el-button type="success"
                     @click="handleAdd">新增</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="table-container">
      <DynamicTable
        ref="dynamicTableRef"
        :data="tableData"
        :dict-types="dictTypes"
        :loading="loading"
        :show-selection="true"
        :show-actions="true"
        :show-pagination="true"
        :pagination="pagination"
        height="calc(100vh - 280px)"
        @selection-change="handleSelectionChange"
        @edit="handleEdit"
        @delete="handleDelete"
        @select-change="handleSelectChange"
        @input-change="handleInputChange"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
      <DynamicTable ref="dynamicTableRef"
                    :data="tableData"
                    :dict-types="dictTypes"
                    :loading="loading"
                    :show-selection="true"
                    :show-actions="true"
                    :show-pagination="true"
                    :pagination="pagination"
                    height="calc(100vh - 280px)"
                    @selection-change="handleSelectionChange"
                    @edit="handleEdit"
                    @delete="handleDelete"
                    @select-change="handleSelectChange"
                    @input-change="handleInputChange"
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange" />
    </div>
    <!-- 新增/编辑对话框 -->
    <el-dialog
      v-model="dialogVisible"
      :title="dialogTitle"
      width="600px"
      append-to-body
    >
      <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
      >
        <el-form-item label="部门" prop="department">
          <el-input v-model="form.department" placeholder="请输入部门" />
    <el-dialog v-model="dialogVisible"
               :title="dialogTitle"
               width="600px"
               append-to-body>
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="100px">
        <el-form-item label="部门"
                      prop="department">
          <el-input v-model="form.department"
                    placeholder="请输入部门" />
        </el-form-item>
        <el-form-item label="姓名" prop="name">
          <el-input v-model="form.name" placeholder="请输入姓名" />
        <el-form-item label="姓名"
                      prop="name">
          <el-input v-model="form.name"
                    placeholder="请输入姓名" />
        </el-form-item>
        <el-form-item label="工号" prop="employeeId">
          <el-input v-model="form.employeeId" placeholder="请输入工号" />
        <el-form-item label="工号"
                      prop="employeeId">
          <el-input v-model="form.employeeId"
                    placeholder="请输入工号" />
        </el-form-item>
        <!-- 动态表单项:根据字典生成 -->
        <el-form-item
          v-for="dictItem in dynamicFormItems"
          :key="dictItem.value"
          :label="dictItem.label"
          :prop="dictItem.value"
        >
          <el-select
            v-model="form[dictItem.value]"
            placeholder="请选择"
            style="width: 100%"
          >
            <el-option
              v-for="option in dictItem.options"
              :key="option.value"
              :label="option.label"
              :value="option.value"
            />
        <el-form-item v-for="dictItem in dynamicFormItems"
                      :key="dictItem.value"
                      :label="dictItem.label"
                      :prop="dictItem.value">
          <el-select v-model="form[dictItem.value]"
                     placeholder="请选择"
                     style="width: 100%">
            <el-option v-for="option in dictItem.options"
                       :key="option.value"
                       :label="option.label"
                       :value="option.value" />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="handleSubmit">确定</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="handleSubmit">确定</el-button>
        </div>
      </template>
    </el-dialog>
@@ -103,252 +93,246 @@
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import DynamicTable from '@/components/DynamicTable/index.vue'
  import { ref, reactive, computed, onMounted } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import DynamicTable from "@/components/DynamicTable/index.vue";
// 响应式数据
const loading = ref(false)
const dialogVisible = ref(false)
const dialogTitle = ref('')
const editIndex = ref(-1)
const selectedRows = ref([])
  // 响应式数据
  const loading = ref(false);
  const dialogVisible = ref(false);
  const dialogTitle = ref("");
  const editIndex = ref(-1);
  const selectedRows = ref([]);
// 搜索表单
const searchForm = reactive({
  department: '',
  name: ''
})
  // 搜索表单
  const searchForm = reactive({
    department: "",
    name: "",
  });
// 表格数据
const tableData = ref([
  {
    id: 1,
    department: '技术部',
    name: '张三',
    employeeId: 'EMP001',
    status: '1',
    level: '2',
    position: '1'
  },
  {
    id: 2,
    department: '人事部',
    name: '李四',
    employeeId: 'EMP002',
    status: '0',
    level: '1',
    position: '2'
  },
  {
    id: 3,
    department: '财务部',
    name: '王五',
    employeeId: 'EMP003',
    status: '1',
    level: '3',
    position: '1'
  }
])
// 字典类型配置
const dictTypes = ref([
  'sys_normal_disable', // 状态字典
  'sys_user_level',     // 级别字典
  'sys_user_position'   // 职位字典
])
// 分页配置
const pagination = reactive({
  current: 1,
  size: 10,
  total: 0
})
// 表单数据
const form = reactive({
  department: '',
  name: '',
  employeeId: '',
  status: '',
  level: '',
  position: ''
})
// 表单验证规则
const rules = {
  department: [
    { required: true, message: '请输入部门', trigger: 'blur' }
  ],
  name: [
    { required: true, message: '请输入姓名', trigger: 'blur' }
  ],
  employeeId: [
    { required: true, message: '请输入工号', trigger: 'blur' }
  ]
}
// 动态表单项
const dynamicFormItems = computed(() => {
  // 这里可以根据字典数据动态生成表单项
  return [
  // 表格数据
  const tableData = ref([
    {
      label: '状态',
      value: 'status',
      options: [
        { label: '启用', value: '1' },
        { label: '禁用', value: '0' }
      ]
      id: 1,
      department: "技术部",
      name: "张三",
      employeeId: "EMP001",
      status: "1",
      level: "2",
      position: "1",
    },
    {
      label: '级别',
      value: 'level',
      options: [
        { label: '初级', value: '1' },
        { label: '中级', value: '2' },
        { label: '高级', value: '3' }
      ]
      id: 2,
      department: "人事部",
      name: "李四",
      employeeId: "EMP002",
      status: "0",
      level: "1",
      position: "2",
    },
    {
      label: '职位',
      value: 'position',
      options: [
        { label: '员工', value: '1' },
        { label: '主管', value: '2' },
        { label: '经理', value: '3' }
      ]
      id: 3,
      department: "财务部",
      name: "王五",
      employeeId: "EMP003",
      status: "1",
      level: "3",
      position: "1",
    },
  ]);
  // 字典类型配置
  const dictTypes = ref([
    "sys_normal_disable", // 状态字典
    "sys_user_level", // 级别字典
    "sys_user_position", // 职位字典
  ]);
  // 分页配置
  const pagination = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  // 表单数据
  const form = reactive({
    department: "",
    name: "",
    employeeId: "",
    status: "",
    level: "",
    position: "",
  });
  // 表单验证规则
  const rules = {
    department: [{ required: true, message: "请输入部门", trigger: "blur" }],
    name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
    employeeId: [{ required: true, message: "请输入工号", trigger: "blur" }],
  };
  // 动态表单项
  const dynamicFormItems = computed(() => {
    // 这里可以根据字典数据动态生成表单项
    return [
      {
        label: "状态",
        value: "status",
        options: [
          { label: "启用", value: "1" },
          { label: "禁用", value: "0" },
        ],
      },
      {
        label: "级别",
        value: "level",
        options: [
          { label: "初级", value: "1" },
          { label: "中级", value: "2" },
          { label: "高级", value: "3" },
        ],
      },
      {
        label: "职位",
        value: "position",
        options: [
          { label: "员工", value: "1" },
          { label: "主管", value: "2" },
          { label: "经理", value: "3" },
        ],
      },
    ];
  });
  // 组件引用
  const dynamicTableRef = ref(null);
  const formRef = ref(null);
  // 事件处理函数
  const handleSearch = () => {
    // 实现搜索逻辑
    console.log("搜索条件:", searchForm);
    ElMessage.success("搜索功能待实现");
  };
  const handleReset = () => {
    searchForm.department = "";
    searchForm.name = "";
  };
  const handleAdd = () => {
    dialogTitle.value = "新增员工";
    editIndex.value = -1;
    resetForm();
    dialogVisible.value = true;
  };
  const handleEdit = (row, index) => {
    dialogTitle.value = "编辑员工";
    editIndex.value = index;
    Object.assign(form, row);
    dialogVisible.value = true;
  };
  const handleDelete = async (row, index) => {
    try {
      await ElMessageBox.confirm("确定要删除这条记录吗?", "提示", {
        type: "warning",
      });
      tableData.value.splice(index, 1);
      ElMessage.success("删除成功");
    } catch (error) {
      // 用户取消删除
    }
  ]
})
  };
// 组件引用
const dynamicTableRef = ref(null)
const formRef = ref(null)
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
// 事件处理函数
const handleSearch = () => {
  // 实现搜索逻辑
  console.log('搜索条件:', searchForm)
  ElMessage.success('搜索功能待实现')
}
  const handleSelectChange = (row, prop, value) => {
    console.log("选择变化:", row, prop, value);
    // 可以在这里处理数据更新逻辑
  };
const handleReset = () => {
  searchForm.department = ''
  searchForm.name = ''
}
  const handleInputChange = (row, prop, value) => {
    console.log("输入变化:", row, prop, value);
    // 可以在这里处理数据更新逻辑
  };
const handleAdd = () => {
  dialogTitle.value = '新增员工'
  editIndex.value = -1
  resetForm()
  dialogVisible.value = true
}
  const handleSizeChange = size => {
    pagination.size = size;
    // 重新加载数据
  };
const handleEdit = (row, index) => {
  dialogTitle.value = '编辑员工'
  editIndex.value = index
  Object.assign(form, row)
  dialogVisible.value = true
}
  const handleCurrentChange = current => {
    pagination.current = current;
    // 重新加载数据
  };
const handleDelete = async (row, index) => {
  try {
    await ElMessageBox.confirm('确定要删除这条记录吗?', '提示', {
      type: 'warning'
    })
    tableData.value.splice(index, 1)
    ElMessage.success('删除成功')
  } catch (error) {
    // 用户取消删除
  }
}
  const handleSubmit = async () => {
    try {
      await formRef.value.validate();
const handleSelectionChange = (selection) => {
  selectedRows.value = selection
}
const handleSelectChange = (row, prop, value) => {
  console.log('选择变化:', row, prop, value)
  // 可以在这里处理数据更新逻辑
}
const handleInputChange = (row, prop, value) => {
  console.log('输入变化:', row, prop, value)
  // 可以在这里处理数据更新逻辑
}
const handleSizeChange = (size) => {
  pagination.size = size
  // 重新加载数据
}
const handleCurrentChange = (current) => {
  pagination.current = current
  // 重新加载数据
}
const handleSubmit = async () => {
  try {
    await formRef.value.validate()
    if (editIndex.value === -1) {
      // 新增
      const newRow = {
        id: Date.now(),
        ...form
      if (editIndex.value === -1) {
        // 新增
        const newRow = {
          id: Date.now(),
          ...form,
        };
        tableData.value.push(newRow);
        ElMessage.success("新增成功");
      } else {
        // 编辑
        Object.assign(tableData.value[editIndex.value], form);
        ElMessage.success("编辑成功");
      }
      tableData.value.push(newRow)
      ElMessage.success('新增成功')
    } else {
      // 编辑
      Object.assign(tableData.value[editIndex.value], form)
      ElMessage.success('编辑成功')
      dialogVisible.value = false;
    } catch (error) {
      console.error("表单验证失败:", error);
    }
    dialogVisible.value = false
  } catch (error) {
    console.error('表单验证失败:', error)
  }
}
  };
const resetForm = () => {
  Object.assign(form, {
    department: '',
    name: '',
    employeeId: '',
    status: '',
    level: '',
    position: ''
  })
  formRef.value?.resetFields()
}
  const resetForm = () => {
    Object.assign(form, {
      department: "",
      name: "",
      employeeId: "",
      status: "",
      level: "",
      position: "",
    });
    formRef.value?.resetFields();
  };
// 组件挂载时初始化数据
onMounted(() => {
  pagination.total = tableData.value.length
})
  // 组件挂载时初始化数据
  onMounted(() => {
    pagination.total = tableData.value.length;
  });
</script>
<style scoped>
.app-container {
  padding: 20px;
}
  .app-container {
    padding: 20px;
  }
.search-form {
  margin-bottom: 20px;
  padding: 20px;
  background-color: #f5f5f5;
  border-radius: 4px;
}
  .search-form {
    margin-bottom: 20px;
    padding: 20px;
    background-color: #f5f5f5;
    border-radius: 4px;
  }
.table-container {
  background-color: #fff;
  border-radius: 4px;
  padding: 20px;
}
  .table-container {
    background-color: #fff;
    border-radius: 4px;
    padding: 20px;
  }
.dialog-footer {
  text-align: right;
}
  .dialog-footer {
    text-align: right;
  }
</style>
src/views/fileManagement/bookshelf/index.vue
@@ -1,52 +1,76 @@
<template>
  <div class="sample">
    <div class="main-content" v-if="!isDetail">
    <div class="main-content"
         v-if="!isDetail">
      <div class="search">
                 <div class="search_thing">
           <div class="search_label">仓库名称:</div>
           <div class="search_input">
             <el-select v-model="entity.warehouseId" placeholder="选择仓库" size="small" @change="warehouseChange">
               <el-option v-for="item in warehouse" :key="item.id" :label="item.label" :value="item.id">
               </el-option>
             </el-select>
           </div>
         </div>
        <div class="search_thing">
          <div class="search_label">货架:</div>
          <div class="search_label">仓库名称:</div>
          <div class="search_input">
            <el-select v-model="entity.shelfId" placeholder="选择货架" size="small" @change="handleShelf">
              <el-option v-for="item in shelf" :key="item.id" :label="item.label" :value="item.id">
            <el-select v-model="entity.warehouseId"
                       placeholder="选择仓库"
                       size="small"
                       @change="warehouseChange">
              <el-option v-for="item in warehouse"
                         :key="item.id"
                         :label="item.label"
                         :value="item.id">
              </el-option>
            </el-select>
          </div>
        </div>
          <!-- <div class="search_thing">
        <div class="search_thing">
          <div class="search_label">货架:</div>
          <div class="search_input">
            <el-select v-model="entity.shelfId"
                       placeholder="选择货架"
                       size="small"
                       @change="handleShelf">
              <el-option v-for="item in shelf"
                         :key="item.id"
                         :label="item.label"
                         :value="item.id">
              </el-option>
            </el-select>
          </div>
        </div>
        <!-- <div class="search_thing">
           <el-button size="small" @click="handleShelf(entity.shelfId,'')">重置</el-button>
           <el-button size="small" type="primary" @click="handleShelf(entity.shelfId)">查询</el-button>
         </div> -->
        <div class="btns">
          <el-button size="small" style="color:#3A7BFA" @click="keepVisible=true">维护</el-button>
          <el-button size="small" style="color:#3A7BFA" @click="warehouseVisible=true,isEdit=false">添加仓库</el-button>
          <el-button size="small" style="color:#3A7BFA" @click="shelvesVisible=true,isEdit=false"
            :disabled="entity.warehouseId==null">添加货架</el-button>
          <el-button size="small"
                     style="color:#3A7BFA"
                     @click="keepVisible=true">维护</el-button>
          <el-button size="small"
                     style="color:#3A7BFA"
                     @click="warehouseVisible=true,isEdit=false">添加仓库</el-button>
          <el-button size="small"
                     style="color:#3A7BFA"
                     @click="shelvesVisible=true,isEdit=false"
                     :disabled="entity.warehouseId==null">添加货架</el-button>
        </div>
      </div>
      <div class="table" v-loading="tableLoading">
        <table class="tables" style="table-layout:fixed;" v-if="tableList.length>0">
      <div class="table"
           v-loading="tableLoading">
        <table class="tables"
               style="table-layout:fixed;"
               v-if="tableList.length>0">
          <tbody>
            <tr v-for="(item,index) in tableList" :key="index">
              <td v-for="(m,i) in item" :key="i" class="content">
            <tr v-for="(item,index) in tableList"
                :key="index">
              <td v-for="(m,i) in item"
                  :key="i"
                  class="content">
                <h4 v-if="m.row!=undefined">{{ m.row }} - {{ m.col }}</h4>
                <ul>
                  <el-tooltip
                  effect="dark"
                  placement="top"
                  v-for="(n,j) in m.documentationDtoList"
                  :key="j">
                  <el-tooltip effect="dark"
                              placement="top"
                              v-for="(n,j) in m.documentationDtoList"
                              :key="j">
                    <template #content><span>{{ n.docName }}</span>
                      <span>&nbsp;[{{ n.docNumber }}]</span></template>
                    <li class="green"
                      @click="handelDetail(n)">
                        @click="handelDetail(n)">
                      <i></i>
                      <span>{{ n.docName }}</span>
                      <span>&nbsp;[{{ n.docNumber }}] <span :style="{ color: getStatusColor(n.docStatus) }">({{ n.docStatus }})</span></span>
@@ -56,39 +80,59 @@
              </td>
            </tr>
            <tr>
              <td v-for="(item,index) in rowList" :key="index" style="background: ghostwhite;height: 20px;">{{ item }}
              <td v-for="(item,index) in rowList"
                  :key="index"
                  style="background: ghostwhite;height: 20px;">{{ item }}
              </td>
            </tr>
          </tbody>
        </table>
        <span v-else style="color: rgb(144, 147, 153);display: inline-block;position: absolute;top: 60%;left: 50%;transform: translate(-50%,-50%);">暂无数据</span>
        <span v-else
              style="color: rgb(144, 147, 153);display: inline-block;position: absolute;top: 60%;left: 50%;transform: translate(-50%,-50%);">暂无数据</span>
      </div>
    </div>
    <Detail v-else @hanldeBack="isDetail=false" :current="current" />
    <Detail v-else
            @hanldeBack="isDetail=false"
            :current="current" />
    <!-- 库位维护对话框 -->
    <el-dialog v-model="keepVisible" title="库位维护" width="350px" :append-to-body="true">
                                                       <el-tree :data="warehouse" ref="tree" node-key="id"
           highlight-current v-if="keepVisible"
           empty-text="暂无数据">
    <el-dialog v-model="keepVisible"
               title="库位维护"
               width="350px"
               :append-to-body="true">
      <el-tree :data="warehouse"
               ref="tree"
               node-key="id"
               highlight-current
               v-if="keepVisible"
               empty-text="暂无数据">
        <template #default="{ node, data }">
          <div class="custom-tree-node" style="width: 100%;">
          <div class="custom-tree-node"
               style="width: 100%;">
            <el-row style="width: 100%;display: flex;align-items: center;">
              <el-col :span="14">
                <span>
                  <el-icon v-if="node.level < 2" class="folder-icon">
                  <el-icon v-if="node.level < 2"
                           class="folder-icon">
                    <FolderOpened />
                  </el-icon>
                  <el-icon v-else class="file-icon">
                  <el-icon v-else
                           class="file-icon">
                    <Document />
                  </el-icon>
                  {{ data.label }}
                </span>
              </el-col>
              <el-col :span="10" v-if="node.level<3">
                <el-button type="link" size="small" :icon="Edit" @click.stop="handleEdit(data,node.level)">
              <el-col :span="10"
                      v-if="node.level<3">
                <el-button type="link"
                           size="small"
                           :icon="Edit"
                           @click.stop="handleEdit(data,node.level)">
                </el-button>
                <el-button type="danger" size="small" :icon="Delete" @click.stop="handleDelete(data,node.level)">
                <el-button type="danger"
                           size="small"
                           :icon="Delete"
                           @click.stop="handleDelete(data,node.level)">
                </el-button>
              </el-col>
            </el-row>
@@ -97,372 +141,393 @@
      </el-tree>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="keepVisible = false">确 定</el-button>
          <el-button @click="keepVisible = false">取 消</el-button>
          <el-button type="primary" @click="keepVisible = false" >确 定</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 仓库新增/修改对话框 -->
    <el-dialog v-model="warehouseVisible" :title="isEdit?'仓库修改':'仓库新增'" width="350px">
    <el-dialog v-model="warehouseVisible"
               :title="isEdit?'仓库修改':'仓库新增'"
               width="350px">
      <el-row>
        <el-col class="search_thing" :span="24">
        <el-col class="search_thing"
                :span="24">
          <div class="search_label"><span class="required-span">* </span>仓库名称:</div>
          <div class="search_input">
            <el-input v-model="name" size="small" @keyup.enter="confirmWarehouse"></el-input>
            <el-input v-model="name"
                      size="small"
                      @keyup.enter="confirmWarehouse"></el-input>
          </div>
        </el-col>
      </el-row>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="confirmWarehouse"
                     :loading="upLoadWarehouse">确 定</el-button>
          <el-button @click="warehouseVisible = false">取 消</el-button>
          <el-button type="primary" @click="confirmWarehouse" :loading="upLoadWarehouse">确 定</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 货架新增/修改对话框 -->
    <el-dialog v-model="shelvesVisible" :title="isEdit?'货架修改':'货架新增'" width="350px">
    <el-dialog v-model="shelvesVisible"
               :title="isEdit?'货架修改':'货架新增'"
               width="350px">
      <el-row>
        <el-col class="search_thing" :span="24">
        <el-col class="search_thing"
                :span="24">
          <div class="search_label"><span class="required-span">* </span>货架名称:</div>
          <div class="search_input">
            <el-input v-model="shelves.name" size="small"></el-input>
            <el-input v-model="shelves.name"
                      size="small"></el-input>
          </div>
        </el-col>
      </el-row>
      <el-row>
        <el-col class="search_thing" :span="24">
        <el-col class="search_thing"
                :span="24">
          <div class="search_label"><span class="required-span">* </span>货架层数:</div>
          <div class="search_input">
            <el-input v-model="shelves.row" size="small"></el-input>
            <el-input v-model="shelves.row"
                      size="small"></el-input>
          </div>
        </el-col>
      </el-row>
      <el-row>
        <el-col class="search_thing" :span="24">
        <el-col class="search_thing"
                :span="24">
          <div class="search_label"><span class="required-span">* </span>货架列数:</div>
          <div class="search_input">
            <el-input v-model="shelves.col" size="small"></el-input>
            <el-input v-model="shelves.col"
                      size="small"></el-input>
          </div>
        </el-col>
      </el-row>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="confirmShelves"
                     :loading="upLoadShelves">确 定</el-button>
          <el-button @click="shelvesVisible = false">取 消</el-button>
          <el-button type="primary" @click="confirmShelves" :loading="upLoadShelves">确 定</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Edit, Delete, FolderOpened, Document } from '@element-plus/icons-vue'
import { getWarehouseList, addWarehouse, updateWarehouse, deleteWarehouse, getWarehouseStructure, addShelf, updateShelf, deleteShelf } from '@/api/fileManagement/bookshelf'
import Detail from './detail.vue'
  import { ref, reactive, onMounted, watch } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { Edit, Delete, FolderOpened, Document } from "@element-plus/icons-vue";
  import {
    getWarehouseList,
    addWarehouse,
    updateWarehouse,
    deleteWarehouse,
    getWarehouseStructure,
    addShelf,
    updateShelf,
    deleteShelf,
  } from "@/api/fileManagement/bookshelf";
  import Detail from "./detail.vue";
// 响应式数据
const entity = reactive({
  warehouseId: null,
  shelfId: null
})
  // 响应式数据
  const entity = reactive({
    warehouseId: null,
    shelfId: null,
  });
const warehouse = ref([])
const shelf = ref([])
const keepVisible = ref(false)
const warehouseVisible = ref(false)
const shelvesVisible = ref(false)
const upLoadWarehouse = ref(false)
const upLoadShelves = ref(false)
const tableList = ref([])
const rowList = ref([])
const value = ref('')
const name = ref('')
const shelves = reactive({})
const isEdit = ref(false)
const isDetail = ref(false)
const currentEdit = ref(null)
const tableLoading = ref(false)
const current = ref({})
  const warehouse = ref([]);
  const shelf = ref([]);
  const keepVisible = ref(false);
  const warehouseVisible = ref(false);
  const shelvesVisible = ref(false);
  const upLoadWarehouse = ref(false);
  const upLoadShelves = ref(false);
  const tableList = ref([]);
  const rowList = ref([]);
  const value = ref("");
  const name = ref("");
  const shelves = reactive({});
  const isEdit = ref(false);
  const isDetail = ref(false);
  const currentEdit = ref(null);
  const tableLoading = ref(false);
  const current = ref({});
// 模板引用
const organization = ref(null)
  // 模板引用
  const organization = ref(null);
// 监听器
watch(isEdit, (newVal) => {
  if (!newVal) {
    Object.keys(shelves).forEach(key => delete shelves[key])
  }
})
// 方法
const selectList = async () => {
  // 这里需要替换为实际的API调用
  const res = await getWarehouseList()
  warehouse.value = res.data
  if (warehouse.value.length == 0) {
    entity.warehouseId = ''
    entity.shelfId = ''
    tableList.value = []
  }
  if (!entity.warehouseId && warehouse.value.length > 0) {
    entity.warehouseId = warehouse.value[0].id
    warehouseChange(entity.warehouseId)
    if (shelf.value.length > 0) {
      entity.shelfId = shelf.value[0].id
      handleShelf(entity.shelfId)
    } else {
      tableList.value = []
  // 监听器
  watch(isEdit, newVal => {
    if (!newVal) {
      Object.keys(shelves).forEach(key => delete shelves[key]);
    }
  } else if (warehouse.value.length > 0) {
    warehouseChange(entity.warehouseId)
    if (shelf.value.length > 0) {
      entity.shelfId = shelf.value[0].id
      handleShelf(entity.shelfId)
    } else {
      tableList.value = []
  });
  // 方法
  const selectList = async () => {
    // 这里需要替换为实际的API调用
    const res = await getWarehouseList();
    warehouse.value = res.data;
    if (warehouse.value.length == 0) {
      entity.warehouseId = "";
      entity.shelfId = "";
      tableList.value = [];
    }
  }
}
const confirmWarehouse = () => {
  if (!name.value) {
    ElMessage.error('请填写仓库名称')
    return
  }
  upLoadWarehouse.value = true
  if (currentEdit.value && currentEdit.value.id) {
    // 修改仓库
    // 这里需要替换为实际的API调用
    updateWarehouse({
      id: currentEdit.value.id,
      warehouseName: name.value
    }).then(res => {
      upLoadWarehouse.value = false
      warehouseVisible.value = false
      currentEdit.value = null
      ElMessage.success('修改成功')
      selectList()
      name.value = ''
      warehouseChange(entity.warehouseId)
    })
  } else {
    // 新增仓库
    // 这里需要替换为实际的API调用
    addWarehouse({
      warehouseName: name.value
    }).then(res => {
      upLoadWarehouse.value = false
      warehouseVisible.value = false
      ElMessage.success('添加成功')
      selectList()
      name.value = ''
      warehouseChange(entity.warehouseId)
    })
  }
}
    if (!entity.warehouseId && warehouse.value.length > 0) {
      entity.warehouseId = warehouse.value[0].id;
      warehouseChange(entity.warehouseId);
      if (shelf.value.length > 0) {
        entity.shelfId = shelf.value[0].id;
        handleShelf(entity.shelfId);
      } else {
        tableList.value = [];
      }
    } else if (warehouse.value.length > 0) {
      warehouseChange(entity.warehouseId);
      if (shelf.value.length > 0) {
        entity.shelfId = shelf.value[0].id;
        handleShelf(entity.shelfId);
      } else {
        tableList.value = [];
      }
    }
  };
const confirmShelves = () => {
  if (!shelves.name) {
    ElMessage.error('请填写货架名称')
    return
  }
  if (!shelves.row) {
    ElMessage.error('请填写货架层数')
    return
  }
  if (!shelves.col) {
    ElMessage.error('请填写货架列数')
    return
  }
  upLoadShelves.value = true
  if (currentEdit.value && currentEdit.value.id) {
    // 修改
    updateShelf({
      id: currentEdit.value.id,
      name: shelves.name,
      row: Number(shelves.row),
      col: Number(shelves.col),
      warehouseId: entity.warehouseId
    }).then(res => {
      upLoadShelves.value = false
      shelvesVisible.value = false
      ElMessage.success('修改成功')
      selectList()
      currentEdit.value = {}
    }).catch(err => {
      upLoadShelves.value = false
      shelvesVisible.value = false
      ElMessage.error('修改失败')
    })
  } else {
    // 新增
    // 这里需要替换为实际的API调用
      addShelf({
      name: shelves.name,
      row: Number(shelves.row),
      col: Number(shelves.col),
      warehouseId: entity.warehouseId
    }).then(res => {
      upLoadShelves.value = false
      shelvesVisible.value = false
      ElMessage.success('添加成功')
      selectList()
      Object.keys(shelves).forEach(key => delete shelves[key])
    }).catch(err => {
      upLoadShelves.value = false
      shelvesVisible.value = false
      ElMessage.error('添加失败')
    })
  }
  warehouseChange(entity.warehouseId)
}
  const confirmWarehouse = () => {
    if (!name.value) {
      ElMessage.error("请填写仓库名称");
      return;
    }
    upLoadWarehouse.value = true;
const handleDelete = (row, level) => {
  ElMessageBox.confirm('是否删除当前数据?', "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning"
  }).then(() => {
    if (level == 1) {
      // 删除仓库
      deleteWarehouse([row.id]).then(res => {
        ElMessage.success('删除成功')
        selectList()
      })
    } else {
      // 删除货架
      deleteShelf({
        id: row.id
    if (currentEdit.value && currentEdit.value.id) {
      // 修改仓库
      // 这里需要替换为实际的API调用
      updateWarehouse({
        id: currentEdit.value.id,
        warehouseName: name.value,
      }).then(res => {
        ElMessage.success('删除成功')
        selectList()
      })
        upLoadWarehouse.value = false;
        warehouseVisible.value = false;
        currentEdit.value = null;
        ElMessage.success("修改成功");
        selectList();
        name.value = "";
        warehouseChange(entity.warehouseId);
      });
    } else {
      // 新增仓库
      // 这里需要替换为实际的API调用
      addWarehouse({
        warehouseName: name.value,
      }).then(res => {
        upLoadWarehouse.value = false;
        warehouseVisible.value = false;
        ElMessage.success("添加成功");
        selectList();
        name.value = "";
        warehouseChange(entity.warehouseId);
      });
    }
    warehouseChange(entity.warehouseId)
  }).catch(() => {})
}
  };
const handleEdit = (data, level) => {
  isEdit.value = true
  if (level == 1) {
    warehouseVisible.value = true
    currentEdit.value = data
    name.value = data.label
  } else {
    shelvesVisible.value = true
    currentEdit.value = data
    Object.assign(shelves, {
      name: data.label,
      row: data.row,
      col: data.col,
      warehouseId: data.warehouseId
    })
  }
}
const handelDetail = (row) => {
  current.value = row
  isDetail.value = true
}
// 根据文档状态返回对应的颜色
const getStatusColor = (status) => {
  if (status === '正常') {
    return '#34BD66' // 绿色
  } else if (status === '借出') {
    return '#F56C6C' // 红色
  }
  return '#606266' // 默认颜色
}
const warehouseChange = (val) => {
tableList.value = []
let map = warehouse.value.find(a => {
  return a && a.id === val ? a : null
})
if (map && map.children) {
  shelf.value = map.children
  entity.shelfId = ''
} else {
  shelf.value = []
}
currentEdit.value = null
}
const handleShelf = async(e) => {
  if (e) {
    tableLoading.value = true
    let data = []
    const res = await getWarehouseStructure({warehouseGoodsShelvesId:e})
    if(res.code == 200){
      data = res.data.map(m=>{
        m.books = m.documentationDtoList|[]
        return m
      })
    }else{
      ElMessage.error(res.message)
  const confirmShelves = () => {
    if (!shelves.name) {
      ElMessage.error("请填写货架名称");
      return;
    }
    setTimeout(() => {
      tableLoading.value = false
      let set = new Set()
      tableList.value = []
      let arr = []
      if (data && data.length > 0) {
        data.forEach(m => {
          if (m && m.row && m.col) {
            set.add(m.col)
            if (arr.length > 0) {
              if (arr.find(n => n.row == m.row)) {
                arr.push(m)
              } else {
                tableList.value.push(arr)
                arr = []
                arr.push(m)
              }
            } else {
              arr.push(m)
            }
          }
    if (!shelves.row) {
      ElMessage.error("请填写货架层数");
      return;
    }
    if (!shelves.col) {
      ElMessage.error("请填写货架列数");
      return;
    }
    upLoadShelves.value = true;
    if (currentEdit.value && currentEdit.value.id) {
      // 修改
      updateShelf({
        id: currentEdit.value.id,
        name: shelves.name,
        row: Number(shelves.row),
        col: Number(shelves.col),
        warehouseId: entity.warehouseId,
      })
        .then(res => {
          upLoadShelves.value = false;
          shelvesVisible.value = false;
          ElMessage.success("修改成功");
          selectList();
          currentEdit.value = {};
        })
        if (arr.length > 0) {
          tableList.value.push(arr)
        .catch(err => {
          upLoadShelves.value = false;
          shelvesVisible.value = false;
          ElMessage.error("修改失败");
        });
    } else {
      // 新增
      // 这里需要替换为实际的API调用
      addShelf({
        name: shelves.name,
        row: Number(shelves.row),
        col: Number(shelves.col),
        warehouseId: entity.warehouseId,
      })
        .then(res => {
          upLoadShelves.value = false;
          shelvesVisible.value = false;
          ElMessage.success("添加成功");
          selectList();
          Object.keys(shelves).forEach(key => delete shelves[key]);
        })
        .catch(err => {
          upLoadShelves.value = false;
          shelvesVisible.value = false;
          ElMessage.error("添加失败");
        });
    }
    warehouseChange(entity.warehouseId);
  };
  const handleDelete = (row, level) => {
    ElMessageBox.confirm("是否删除当前数据?", "警告", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        if (level == 1) {
          // 删除仓库
          deleteWarehouse([row.id]).then(res => {
            ElMessage.success("删除成功");
            selectList();
          });
        } else {
          // 删除货架
          deleteShelf({
            id: row.id,
          }).then(res => {
            ElMessage.success("删除成功");
            selectList();
          });
        }
        warehouseChange(entity.warehouseId);
      })
      .catch(() => {});
  };
  const handleEdit = (data, level) => {
    isEdit.value = true;
    if (level == 1) {
      warehouseVisible.value = true;
      currentEdit.value = data;
      name.value = data.label;
    } else {
      shelvesVisible.value = true;
      currentEdit.value = data;
      Object.assign(shelves, {
        name: data.label,
        row: data.row,
        col: data.col,
        warehouseId: data.warehouseId,
      });
    }
  };
  const handelDetail = row => {
    current.value = row;
    isDetail.value = true;
  };
  // 根据文档状态返回对应的颜色
  const getStatusColor = status => {
    if (status === "正常") {
      return "#34BD66"; // 绿色
    } else if (status === "借出") {
      return "#F56C6C"; // 红色
    }
    return "#606266"; // 默认颜色
  };
  const warehouseChange = val => {
    tableList.value = [];
    let map = warehouse.value.find(a => {
      return a && a.id === val ? a : null;
    });
    if (map && map.children) {
      shelf.value = map.children;
      entity.shelfId = "";
    } else {
      shelf.value = [];
    }
    currentEdit.value = null;
  };
  const handleShelf = async e => {
    if (e) {
      tableLoading.value = true;
      let data = [];
      const res = await getWarehouseStructure({ warehouseGoodsShelvesId: e });
      if (res.code == 200) {
        data = res.data.map(m => {
          m.books = m.documentationDtoList | [];
          return m;
        });
      } else {
        ElMessage.error(res.message);
      }
      rowList.value = []
      for (let i = 0; i < set.size; i++) {
        rowList.value.push(`${i + 1} 列`)
      }
      console.log(6666, tableList.value,rowList.value,data)
    }, 1000)
  }
}
      setTimeout(() => {
        tableLoading.value = false;
        let set = new Set();
        tableList.value = [];
        let arr = [];
        if (data && data.length > 0) {
          data.forEach(m => {
            if (m && m.row && m.col) {
              set.add(m.col);
              if (arr.length > 0) {
                if (arr.find(n => n.row == m.row)) {
                  arr.push(m);
                } else {
                  tableList.value.push(arr);
                  arr = [];
                  arr.push(m);
                }
              } else {
                arr.push(m);
              }
            }
          });
          if (arr.length > 0) {
            tableList.value.push(arr);
          }
        }
// 生命周期
onMounted(() => {
  selectList()
})
        rowList.value = [];
        for (let i = 0; i < set.size; i++) {
          rowList.value.push(`${i + 1} 列`);
        }
        console.log(6666, tableList.value, rowList.value, data);
      }, 1000);
    }
  };
  // 生命周期
  onMounted(() => {
    selectList();
  });
</script>
<style scoped>
@@ -597,23 +662,23 @@
  }
  li:hover i {
    background: #3A7BFA;
    background: #3a7bfa;
  }
  li:hover .num {
    color: #3A7BFA;
    color: #3a7bfa;
  }
  .green {
    background: #E0F6EA;
    background: #e0f6ea;
  }
  .green i {
    background: #34BD66;
    background: #34bd66;
  }
  .green .num {
    color: #34BD66;
    color: #34bd66;
  }
  .el-dialog {
@@ -624,7 +689,7 @@
    display: flex;
    align-items: center;
    font-size: 14px;
    color: #3A7BFA;
    color: #3a7bfa;
    position: absolute;
    top: 23px;
    right: 54px;
src/views/inventoryManagement/stockWarning/index.vue
@@ -306,8 +306,9 @@
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDialog">取消</el-button>
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDialog">取消</el-button>
        </div>
      </template>
    </el-dialog>
src/views/oaSystem/projectManagement/components/milestoneList.vue
@@ -2,18 +2,21 @@
<template>
  <div class="milestone-list-container">
    <el-timeline>
      <el-timeline-item
        v-for="milestone in milestoneList"
        :key="milestone.phaseId"
        :timestamp="milestone.endDate"
      >
      <el-timeline-item v-for="milestone in milestoneList"
                        :key="milestone.phaseId"
                        :timestamp="milestone.endDate">
        <el-card>
          <template #header>
            <div class="card-header">
              <span>{{ milestone.phaseName }}</span>
              <div class="milestone-actions">
                <el-button type="text" size="small" @click="handleEdit(milestone)">编辑</el-button>
                <el-button type="text" size="small" @click="handleDelete(milestone)" danger>删除</el-button>
                <el-button type="text"
                           size="small"
                           @click="handleEdit(milestone)">编辑</el-button>
                <el-button type="text"
                           size="small"
                           @click="handleDelete(milestone)"
                           danger>删除</el-button>
              </div>
            </div>
          </template>
@@ -26,66 +29,67 @@
        </el-card>
      </el-timeline-item>
    </el-timeline>
    <!-- 无里程碑时的提示 -->
    <div v-if="milestoneList.length === 0" class="empty-tip">
    <div v-if="milestoneList.length === 0"
         class="empty-tip">
      <el-empty description="暂无里程碑数据" />
    </div>
    <!-- 编辑里程碑对话框 -->
    <el-dialog
      v-model="dialogVisible"
      :title="'编辑里程碑: ' + (form.phaseName || '')"
      width="600px"
      :close-on-click-modal="false"
    >
      <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
      >
        <el-form-item label="里程碑名称" prop="phaseName">
          <el-input v-model="form.phaseName" placeholder="请输入里程碑名称" />
    <el-dialog v-model="dialogVisible"
               :title="'编辑里程碑: ' + (form.phaseName || '')"
               width="600px"
               :close-on-click-modal="false">
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="100px">
        <el-form-item label="里程碑名称"
                      prop="phaseName">
          <el-input v-model="form.phaseName"
                    placeholder="请输入里程碑名称" />
        </el-form-item>
        <el-row :gutter="20">
      <el-col :span="12">
        <el-form-item label="开始日期" prop="startDate">
          <el-date-picker
            v-model="form.startDate"
            type="date"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
            placeholder="选择开始日期"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="结束日期" prop="endDate">
          <el-date-picker
            v-model="form.endDate"
            type="date"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
            placeholder="选择结束日期"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
    </el-row>
        <el-form-item label="状态" prop="status">
          <el-select v-model="form.status" placeholder="请选择状态">
            <el-option label="未开始" value="notStarted" />
            <el-option label="已完成" value="completed" />
            <el-option label="已延迟" value="delayed" />
          <el-col :span="12">
            <el-form-item label="开始日期"
                          prop="startDate">
              <el-date-picker v-model="form.startDate"
                              type="date"
                              format="YYYY-MM-DD"
                              value-format="YYYY-MM-DD"
                              placeholder="选择开始日期"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="结束日期"
                          prop="endDate">
              <el-date-picker v-model="form.endDate"
                              type="date"
                              format="YYYY-MM-DD"
                              value-format="YYYY-MM-DD"
                              placeholder="选择结束日期"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="状态"
                      prop="status">
          <el-select v-model="form.status"
                     placeholder="请选择状态">
            <el-option label="未开始"
                       value="notStarted" />
            <el-option label="已完成"
                       value="completed" />
            <el-option label="已延迟"
                       value="delayed" />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitEditForm">确定</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="submitEditForm">确定</el-button>
        </div>
      </template>
    </el-dialog>
@@ -93,197 +97,203 @@
</template>
<script setup>
import { ref, onMounted, watch, reactive } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getProject, listProjectPhase, updateProjectPhase,delProjectPhase } from '@/api/oaSystem/projectManagement';
  import { ref, onMounted, watch, reactive } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    getProject,
    listProjectPhase,
    updateProjectPhase,
    delProjectPhase,
  } from "@/api/oaSystem/projectManagement";
const props = defineProps({
  projectId: {
    type: String,
    required: true
  }
});
const emit = defineEmits(['refresh']);
const milestoneList = ref([]);
const dialogVisible = ref(false);
const formRef = ref(null);
const form = reactive({
  phaseId: '',
  phaseName: '',
  startDate: '',
  endDate: '',
  status: 'notStarted',
  projectId: props.projectId
});
// 表单验证规则
const rules = {
  phaseName: [
    { required: true, message: '请输入里程碑名称', trigger: 'blur' },
    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
  ],
  status: [
    { required: true, message: '请选择状态', trigger: 'change' }
  ]
};
// 获取里程碑列表
const getMilestoneList = async () => {
  try {
    listProjectPhase(props.projectId).then(res => {
      milestoneList.value = res.data.rows || res.data;
      // 按目标日期排序
      // milestoneList.value.sort((a, b) => new Date(a.endDate) - new Date(b.endDate));
    })
  } catch (error) {
    ElMessage.error('获取里程碑列表失败');
    console.error('获取里程碑列表失败:', error);
  }
};
// 编辑里程碑
const handleEdit = (milestone) => {
  // 复制里程碑数据到表单
  Object.assign(form, {
    phaseId: milestone.phaseId,
    phaseName: milestone.phaseName,
    description: milestone.description,
    endDate: milestone.endDate,
    status: milestone.status,
    projectId: props.projectId
  const props = defineProps({
    projectId: {
      type: String,
      required: true,
    },
  });
  dialogVisible.value = true;
};
// 提交编辑表单
const submitEditForm = async () => {
  try {
    await formRef.value.validate();
    // 发送更新请求
    const res = await updateProjectPhase(form);
    if (res.code === 200) {
      ElMessage.success('里程碑编辑成功');
      dialogVisible.value = false;
      getMilestoneList(); // 刷新列表
      emit('refresh'); // 通知父组件刷新
    } else {
      ElMessage.error(res.msg || '里程碑编辑失败');
    }
  } catch (error) {
    if (error.name === 'ValidationError') {
      // 表单验证失败,Element Plus会自动提示
      return;
    }
    ElMessage.error('里程碑编辑失败');
    console.error('编辑里程碑失败:', error);
  }
};
  const emit = defineEmits(["refresh"]);
// 删除里程碑
const handleDelete = (milestone) => {
  ElMessageBox.confirm(
    `确定要删除里程碑 "${milestone.phaseName}" 吗?删除后将无法恢复。`,
    '删除确认',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
  const milestoneList = ref([]);
  const dialogVisible = ref(false);
  const formRef = ref(null);
  const form = reactive({
    phaseId: "",
    phaseName: "",
    startDate: "",
    endDate: "",
    status: "notStarted",
    projectId: props.projectId,
  });
  // 表单验证规则
  const rules = {
    phaseName: [
      { required: true, message: "请输入里程碑名称", trigger: "blur" },
      { min: 2, max: 50, message: "长度在 2 到 50 个字符", trigger: "blur" },
    ],
    status: [{ required: true, message: "请选择状态", trigger: "change" }],
  };
  // 获取里程碑列表
  const getMilestoneList = async () => {
    try {
      listProjectPhase(props.projectId).then(res => {
        milestoneList.value = res.data.rows || res.data;
        // 按目标日期排序
        // milestoneList.value.sort((a, b) => new Date(a.endDate) - new Date(b.endDate));
      });
    } catch (error) {
      ElMessage.error("获取里程碑列表失败");
      console.error("获取里程碑列表失败:", error);
    }
  )
    .then(async () => {
      try {
        // 调用删除API
        const res = await delProjectPhase(milestone.phaseId);
        if (res.code === 200) {
          ElMessage.success('里程碑删除成功');
          getMilestoneList(); // 刷新列表
          emit('refresh'); // 通知父组件刷新
        } else {
          ElMessage.error(res.msg || '里程碑删除失败');
        }
      } catch (error) {
        ElMessage.error('里程碑删除失败');
        console.error('删除里程碑失败:', error);
      }
    })
    .catch(() => {
      // 用户取消删除
      ElMessage.info('已取消删除');
  };
  // 编辑里程碑
  const handleEdit = milestone => {
    // 复制里程碑数据到表单
    Object.assign(form, {
      phaseId: milestone.phaseId,
      phaseName: milestone.phaseName,
      description: milestone.description,
      endDate: milestone.endDate,
      status: milestone.status,
      projectId: props.projectId,
    });
};
// 获取状态标签类型
const getStatusType = (status) => {
  const statusTypeMap = {
    notStarted: 'info',
    completed: 'success',
    delayed: 'danger'
    dialogVisible.value = true;
  };
  return statusTypeMap[status] || 'default';
};
// 获取状态文本
const getStatusText = (status) => {
  const statusTextMap = {
    notStarted: '未开始',
    completed: '已完成',
    delayed: '已延迟'
  // 提交编辑表单
  const submitEditForm = async () => {
    try {
      await formRef.value.validate();
      // 发送更新请求
      const res = await updateProjectPhase(form);
      if (res.code === 200) {
        ElMessage.success("里程碑编辑成功");
        dialogVisible.value = false;
        getMilestoneList(); // 刷新列表
        emit("refresh"); // 通知父组件刷新
      } else {
        ElMessage.error(res.msg || "里程碑编辑失败");
      }
    } catch (error) {
      if (error.name === "ValidationError") {
        // 表单验证失败,Element Plus会自动提示
        return;
      }
      ElMessage.error("里程碑编辑失败");
      console.error("编辑里程碑失败:", error);
    }
  };
  return statusTextMap[status] || status;
};
// 监听项目ID变化
watch(() => props.projectId, () => {
  if (props.projectId) {
    getMilestoneList();
  }
});
  // 删除里程碑
  const handleDelete = milestone => {
    ElMessageBox.confirm(
      `确定要删除里程碑 "${milestone.phaseName}" 吗?删除后将无法恢复。`,
      "删除确认",
      {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }
    )
      .then(async () => {
        try {
          // 调用删除API
          const res = await delProjectPhase(milestone.phaseId);
// 初始化
onMounted(() => {
  if (props.projectId) {
    getMilestoneList();
  }
});
          if (res.code === 200) {
            ElMessage.success("里程碑删除成功");
            getMilestoneList(); // 刷新列表
            emit("refresh"); // 通知父组件刷新
          } else {
            ElMessage.error(res.msg || "里程碑删除失败");
          }
        } catch (error) {
          ElMessage.error("里程碑删除失败");
          console.error("删除里程碑失败:", error);
        }
      })
      .catch(() => {
        // 用户取消删除
        ElMessage.info("已取消删除");
      });
  };
  // 获取状态标签类型
  const getStatusType = status => {
    const statusTypeMap = {
      notStarted: "info",
      completed: "success",
      delayed: "danger",
    };
    return statusTypeMap[status] || "default";
  };
  // 获取状态文本
  const getStatusText = status => {
    const statusTextMap = {
      notStarted: "未开始",
      completed: "已完成",
      delayed: "已延迟",
    };
    return statusTextMap[status] || status;
  };
  // 监听项目ID变化
  watch(
    () => props.projectId,
    () => {
      if (props.projectId) {
        getMilestoneList();
      }
    }
  );
  // 初始化
  onMounted(() => {
    if (props.projectId) {
      getMilestoneList();
    }
  });
</script>
<style scoped>
.milestone-list-container {
  padding: 10px 0;
}
  .milestone-list-container {
    padding: 10px 0;
  }
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
.milestone-actions {
  display: flex;
  gap: 10px;
}
  .milestone-actions {
    display: flex;
    gap: 10px;
  }
.milestone-content {
  padding: 10px 0;
}
  .milestone-content {
    padding: 10px 0;
  }
.milestone-status {
  margin-top: 10px;
}
  .milestone-status {
    margin-top: 10px;
  }
.empty-tip {
  margin-top: 40px;
  text-align: center;
}
  .empty-tip {
    margin-top: 40px;
    text-align: center;
  }
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
  }
</style>
src/views/oaSystem/projectManagement/components/taskTree.vue
@@ -2,69 +2,98 @@
  <div class="task-tree-container">
    <!-- 任务树操作按钮 -->
    <div class="tree-actions mb10">
      <el-button type="primary" size="small" icon="Plus" @click="handleAddTask">添加任务</el-button>
      <el-button type="success" size="small" icon="RefreshRight" @click="refreshTree">刷新</el-button>
      <el-button type="info" size="small" icon="Filter" @click="toggleFilter">
      <el-button type="primary"
                 size="small"
                 icon="Plus"
                 @click="handleAddTask">添加任务</el-button>
      <el-button type="success"
                 size="small"
                 icon="RefreshRight"
                 @click="refreshTree">刷新</el-button>
      <el-button type="info"
                 size="small"
                 icon="Filter"
                 @click="toggleFilter">
        {{ showFilter ? '隐藏筛选' : '显示筛选' }}
      </el-button>
    </div>
    <!-- 筛选条件 -->
    <div v-if="showFilter" class="filter-section mb10">
      <el-form :inline="true" :model="filterParams">
    <div v-if="showFilter"
         class="filter-section mb10">
      <el-form :inline="true"
               :model="filterParams">
        <el-form-item label="任务状态">
          <el-select v-model="filterParams.status" placeholder="全部" clearable style="width: 120px">
            <el-option label="未开始" value="notStarted" />
            <el-option label="进行中" value="inProgress" />
            <el-option label="已完成" value="completed" />
            <el-option label="已逾期" value="overdue" />
          <el-select v-model="filterParams.status"
                     placeholder="全部"
                     clearable
                     style="width: 120px">
            <el-option label="未开始"
                       value="notStarted" />
            <el-option label="进行中"
                       value="inProgress" />
            <el-option label="已完成"
                       value="completed" />
            <el-option label="已逾期"
                       value="overdue" />
          </el-select>
        </el-form-item>
        <el-form-item label="负责人">
          <el-input v-model="filterParams.assignee" placeholder="输入负责人" clearable style="width: 150px" />
          <el-input v-model="filterParams.assignee"
                    placeholder="输入负责人"
                    clearable
                    style="width: 150px" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" size="small" @click="filterTree">筛选</el-button>
          <el-button size="small" @click="resetFilter">重置</el-button>
          <el-button type="primary"
                     size="small"
                     @click="filterTree">筛选</el-button>
          <el-button size="small"
                     @click="resetFilter">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <!-- 任务树 -->
    <div class="tree-content">
      <el-tree
        v-loading="loading"
        :data="taskTreeData"
        :props="defaultProps"
        :expand-on-click-node="false"
        node-key="nodeId"
        ref="treeRef"
        @node-contextmenu="handleContextMenu"
        @node-click="handleNodeClick"
      >
      <el-tree v-loading="loading"
               :data="taskTreeData"
               :props="defaultProps"
               :expand-on-click-node="false"
               node-key="nodeId"
               ref="treeRef"
               @node-contextmenu="handleContextMenu"
               @node-click="handleNodeClick">
        <template #default="{ node, data }">
          <!-- 节点内容 -->
          <div class="tree-node-content" :class="{ 'phase-node': data.type === 'phase', 'task-node': data.type === 'task' }">
          <div class="tree-node-content"
               :class="{ 'phase-node': data.type === 'phase', 'task-node': data.type === 'task' }">
            <!-- 节点图标 -->
            <div class="node-icon">
              <i v-if="data.type === 'phase'" class="el-icon-folder text-primary" />
              <i v-else-if="data.status === 'completed'" class="el-icon-circle-check text-success" />
              <i v-else-if="data.status === 'inProgress'" class="el-icon-circle-check text-primary" />
              <i v-else-if="data.status === 'overdue'" class="el-icon-alarm-clock text-danger" />
              <i v-else class="el-icon-circle-close text-gray-400" />
              <i v-if="data.type === 'phase'"
                 class="el-icon-folder text-primary" />
              <i v-else-if="data.status === 'completed'"
                 class="el-icon-circle-check text-success" />
              <i v-else-if="data.status === 'inProgress'"
                 class="el-icon-circle-check text-primary" />
              <i v-else-if="data.status === 'overdue'"
                 class="el-icon-alarm-clock text-danger" />
              <i v-else
                 class="el-icon-circle-close text-gray-400" />
            </div>
            <!-- 节点标题和描述 -->
            <div class="node-info">
              <div class="node-title" :class="{ 'overdue-title': data.type === 'task' && data.status === 'overdue' }">
              <div class="node-title"
                   :class="{ 'overdue-title': data.type === 'task' && data.status === 'overdue' }">
                {{ node.label }}
                <span v-if="data.type === 'task' && data.priority === 'high'" class="priority-tag">高优</span>
                <span v-else-if="data.type === 'task' && data.priority === 'medium'" class="priority-tag medium">中优</span>
                <span v-if="data.type === 'task' && data.priority === 'high'"
                      class="priority-tag">高优</span>
                <span v-else-if="data.type === 'task' && data.priority === 'medium'"
                      class="priority-tag medium">中优</span>
              </div>
              <div v-if="data.description" class="node-description">{{ data.description }}</div>
              <div v-if="data.description"
                   class="node-description">{{ data.description }}</div>
              <!-- 任务元信息 -->
              <div v-if="data.type === 'task'" class="task-meta">
              <div v-if="data.type === 'task'"
                   class="task-meta">
                <span class="meta-item">
                  <i class="el-icon-user"></i>
                  {{ data.assigneeName || '未分配' }}
@@ -75,97 +104,115 @@
                </span>
              </div>
            </div>
            <!-- 任务进度条 -->
            <div v-if="data.type === 'task'" class="task-progress">
              <el-progress :percentage="data.progress || 0" :stroke-width="4" :show-text="false" />
            <div v-if="data.type === 'task'"
                 class="task-progress">
              <el-progress :percentage="data.progress || 0"
                           :stroke-width="4"
                           :show-text="false" />
            </div>
            <!-- 操作按钮 -->
            <div class="node-actions">
              <el-button
                v-if="data.type === 'task'"
                type="text"
                size="small"
                icon="Edit"
                @click.stop="handleEditTask(data)"
                v-hasPermi="['oaSystem:task:edit']"
              />
              <el-button
                v-if="data.type === 'phase'"
                type="text"
                size="small"
                icon="Plus"
                @click.stop="handleAddTaskUnderPhase(data)"
                v-hasPermi="['oaSystem:task:add']"
              />
              <el-button
                type="text"
                size="small"
                icon="Delete"
                @click.stop="handleDeleteNode(data)"
                v-hasPermi="['oaSystem:task:remove']"
              />
              <el-button v-if="data.type === 'task'"
                         type="text"
                         size="small"
                         icon="Edit"
                         @click.stop="handleEditTask(data)"
                         v-hasPermi="['oaSystem:task:edit']" />
              <el-button v-if="data.type === 'phase'"
                         type="text"
                         size="small"
                         icon="Plus"
                         @click.stop="handleAddTaskUnderPhase(data)"
                         v-hasPermi="['oaSystem:task:add']" />
              <el-button type="text"
                         size="small"
                         icon="Delete"
                         @click.stop="handleDeleteNode(data)"
                         v-hasPermi="['oaSystem:task:remove']" />
            </div>
          </div>
        </template>
      </el-tree>
    </div>
    <!-- 右键菜单 -->
    <div v-if="showContextMenu" :style="contextMenuStyle" class="context-menu">
    <div v-if="showContextMenu"
         :style="contextMenuStyle"
         class="context-menu">
      <el-menu @select="handleContextMenuSelect">
        <el-menu-item v-if="selectedNode.type === 'task'" index="edit">编辑任务</el-menu-item>
        <el-menu-item v-if="selectedNode.type === 'phase'" index="addTask">添加子任务</el-menu-item>
        <el-menu-item v-if="selectedNode.type === 'task'"
                      index="edit">编辑任务</el-menu-item>
        <el-menu-item v-if="selectedNode.type === 'phase'"
                      index="addTask">添加子任务</el-menu-item>
        <el-menu-item index="delete">删除</el-menu-item>
        <el-menu-item index="expandAll">展开全部</el-menu-item>
        <el-menu-item index="collapseAll">收起全部</el-menu-item>
      </el-menu>
    </div>
    <!-- 任务表单对话框 -->
    <el-dialog :title="dialogTitle" v-model="dialogOpen" width="600px" append-to-body>
      <el-form ref="taskFormRef" :model="taskForm" :rules="taskRules" label-width="80px">
        <el-form-item label="任务名称" prop="taskName">
          <el-input v-model="taskForm.taskName" placeholder="请输入任务名称" />
    <el-dialog :title="dialogTitle"
               v-model="dialogOpen"
               width="600px"
               append-to-body>
      <el-form ref="taskFormRef"
               :model="taskForm"
               :rules="taskRules"
               label-width="80px">
        <el-form-item label="任务名称"
                      prop="taskName">
          <el-input v-model="taskForm.taskName"
                    placeholder="请输入任务名称" />
        </el-form-item>
        <el-form-item label="负责人" prop="assigneeId">
          <el-input v-model="taskForm.assigneeId" placeholder="请输入负责人ID" />
        <el-form-item label="负责人"
                      prop="assigneeId">
          <el-input v-model="taskForm.assigneeId"
                    placeholder="请输入负责人ID" />
        </el-form-item>
        <el-form-item label="开始日期" prop="startDate">
          <el-date-picker
            v-model="taskForm.startDate"
            type="date"
            placeholder="选择开始日期"
            style="width: 100%"
          />
        <el-form-item label="开始日期"
                      prop="startDate">
          <el-date-picker v-model="taskForm.startDate"
                          type="date"
                          placeholder="选择开始日期"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="结束日期" prop="endDate">
          <el-date-picker
            v-model="taskForm.endDate"
            type="date"
            placeholder="选择结束日期"
            style="width: 100%"
          />
        <el-form-item label="结束日期"
                      prop="endDate">
          <el-date-picker v-model="taskForm.endDate"
                          type="date"
                          placeholder="选择结束日期"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="优先级" prop="priority">
          <el-select v-model="taskForm.priority" placeholder="选择优先级">
            <el-option label="低" value="low" />
            <el-option label="中" value="medium" />
            <el-option label="高" value="high" />
        <el-form-item label="优先级"
                      prop="priority">
          <el-select v-model="taskForm.priority"
                     placeholder="选择优先级">
            <el-option label="低"
                       value="low" />
            <el-option label="中"
                       value="medium" />
            <el-option label="高"
                       value="high" />
          </el-select>
        </el-form-item>
        <el-form-item label="进度" prop="progress">
          <el-input-number v-model="taskForm.progress" :min="0" :max="100" style="width: 100%" />
        <el-form-item label="进度"
                      prop="progress">
          <el-input-number v-model="taskForm.progress"
                           :min="0"
                           :max="100"
                           style="width: 100%" />
        </el-form-item>
        <el-form-item label="描述" prop="description">
          <el-input v-model="taskForm.description" type="textarea" placeholder="请输入任务描述" />
        <el-form-item label="描述"
                      prop="description">
          <el-input v-model="taskForm.description"
                    type="textarea"
                    placeholder="请输入任务描述" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitTaskForm">确定</el-button>
          <el-button @click="dialogOpen = false">取消</el-button>
          <el-button type="primary" @click="submitTaskForm">确定</el-button>
        </div>
      </template>
    </el-dialog>
@@ -173,662 +220,676 @@
</template>
<script setup>
import { ref, reactive, computed, watch, onMounted } from 'vue';
import { ElMessage, ElMessageBox, ElMenu, ElMenuItem } from 'element-plus';
// import { getProject, addTask, updateTask, deleteTask, deletePhase } from '@/api/oaSystem/projectManagement';
  import { ref, reactive, computed, watch, onMounted } from "vue";
  import { ElMessage, ElMessageBox, ElMenu, ElMenuItem } from "element-plus";
  // import { getProject, addTask, updateTask, deleteTask, deletePhase } from '@/api/oaSystem/projectManagement';
const props = defineProps({
  projectId: {
    type: String,
    required: true
  }
});
const emit = defineEmits(['refresh']);
// 组件状态
const loading = ref(false);
const treeRef = ref();
const showContextMenu = ref(false);
const contextMenuStyle = ref({});
const selectedNode = ref({});
const showFilter = ref(false);
const dialogOpen = ref(false);
const dialogTitle = ref('');
const taskFormRef = ref();
// 筛选参数
const filterParams = reactive({
  status: '',
  assignee: ''
});
// 任务表单数据
const taskForm = reactive({
  taskId: undefined,
  taskName: '',
  description: '',
  startDate: '',
  endDate: '',
  assigneeId: '',
  assigneeName: '',
  status: 'notStarted',
  progress: 0,
  priority: 'medium',
  phaseId: '',
  projectId: props.projectId
});
// 表单验证规则
const taskRules = {
  taskName: [
    { required: true, message: '任务名称不能为空', trigger: 'blur' },
    { min: 2, max: 50, message: '任务名称长度在 2 到 50 个字符', trigger: 'blur' }
  ],
  startDate: [
    { required: true, message: '开始日期不能为空', trigger: 'change' }
  ],
  endDate: [
    { required: true, message: '结束日期不能为空', trigger: 'change' }
  ],
  assigneeId: [
    { required: true, message: '负责人不能为空', trigger: 'blur' }
  ],
  progress: [
    { required: true, message: '进度不能为空', trigger: 'blur' },
    { type: 'number', min: 0, max: 100, message: '进度必须在 0 到 100 之间', trigger: 'blur' }
  ]
};
// 任务树数据
const rawTaskTreeData = ref([]);
// 模拟任务数据
const mockTaskData = {
  'PRJ2023001': [
    {
      phaseId: 'PHASE001',
      phaseName: '需求分析',
      startDate: '2023-11-01',
      endDate: '2023-11-15',
      status: 'completed',
      tasks: [
        {
          taskId: 'TASK001',
          taskName: '需求调研',
          description: '调研用户需求和业务流程',
          startDate: '2023-11-01',
          endDate: '2023-11-05',
          assigneeId: 'USER001',
          assigneeName: '张三',
          status: 'completed',
          progress: 100,
          priority: 'medium'
        },
        {
          taskId: 'TASK002',
          taskName: '需求文档编写',
          description: '编写详细的需求规格说明书',
          startDate: '2023-11-06',
          endDate: '2023-11-15',
          assigneeId: 'USER002',
          assigneeName: '李四',
          status: 'completed',
          progress: 100,
          priority: 'high'
        }
      ]
  const props = defineProps({
    projectId: {
      type: String,
      required: true,
    },
    {
      phaseId: 'PHASE002',
      phaseName: '系统设计',
      startDate: '2023-11-16',
      endDate: '2023-12-10',
      status: 'completed',
      tasks: [
        {
          taskId: 'TASK003',
          taskName: '系统架构设计',
          description: '设计系统整体架构',
          startDate: '2023-11-16',
          endDate: '2023-11-25',
          assigneeId: 'USER003',
          assigneeName: '王五',
          status: 'completed',
          progress: 100,
          priority: 'high'
        },
        {
          taskId: 'TASK004',
          taskName: '数据库设计',
          description: '设计数据库表结构和关系',
          startDate: '2023-11-26',
          endDate: '2023-12-10',
          assigneeId: 'USER004',
          assigneeName: '赵六',
          status: 'completed',
          progress: 100,
          priority: 'medium'
        }
      ]
    },
    {
      phaseId: 'PHASE003',
      phaseName: '开发实现',
      startDate: '2023-12-11',
      endDate: '2024-01-31',
      status: 'inProgress',
      tasks: [
        {
          taskId: 'TASK005',
          taskName: '前端开发',
          description: '开发用户界面和交互逻辑',
          startDate: '2023-12-11',
          endDate: '2024-01-15',
          assigneeId: 'USER005',
          assigneeName: '钱七',
          status: 'inProgress',
          progress: 70,
          priority: 'high'
        },
        {
          taskId: 'TASK006',
          taskName: '后端开发',
          description: '开发业务逻辑和API接口',
          startDate: '2023-12-11',
          endDate: '2024-01-20',
          assigneeId: 'USER006',
          assigneeName: '孙八',
          status: 'inProgress',
          progress: 60,
          priority: 'high'
        }
      ]
    }
  ],
  // 默认数据
  default: [
    {
      phaseId: 'PHASE_DEFAULT1',
      phaseName: '准备阶段',
      startDate: '2023-01-01',
      endDate: '2023-03-31',
      status: 'completed',
      tasks: [
        {
          taskId: 'TASK_DEFAULT1',
          taskName: '项目启动',
          description: '召开项目启动会议',
          startDate: '2023-01-01',
          endDate: '2023-01-05',
          assigneeId: 'USER_DEFAULT1',
          assigneeName: '负责人A',
          status: 'completed',
          progress: 100,
          priority: 'high'
        }
      ]
    },
    {
      phaseId: 'PHASE_DEFAULT2',
      phaseName: '执行阶段',
      startDate: '2023-04-01',
      endDate: '2023-09-30',
      status: 'inProgress',
      tasks: [
        {
          taskId: 'TASK_DEFAULT2',
          taskName: '核心功能开发',
          description: '开发系统核心功能模块',
          startDate: '2023-04-01',
          endDate: '2023-06-30',
          assigneeId: 'USER_DEFAULT2',
          assigneeName: '负责人B',
          status: 'inProgress',
          progress: 50,
          priority: 'high'
        }
      ]
    }
  ]
};
const taskTreeData = computed(() => {
  // 应用筛选条件
  if (!showFilter.value || (!filterParams.status && !filterParams.assignee)) {
    return rawTaskTreeData.value;
  }
  // 深拷贝原始数据以避免修改
  const filteredData = JSON.parse(JSON.stringify(rawTaskTreeData.value));
  // 递归筛选节点
  const filterNodes = (nodes) => {
    const result = [];
    nodes.forEach(node => {
      // 对于阶段节点,检查其子任务是否符合筛选条件
      if (node.type === 'phase' && node.children) {
        const filteredChildren = filterNodes(node.children);
        if (filteredChildren.length > 0) {
          // 保留至少有一个子任务符合条件的阶段
          node.children = filteredChildren;
          result.push(node);
        }
      }
      // 对于任务节点,直接应用筛选条件
      else if (node.type === 'task') {
        const statusMatch = !filterParams.status || node.status === filterParams.status;
        const assigneeMatch = !filterParams.assignee ||
          (node.assigneeName && node.assigneeName.includes(filterParams.assignee));
        if (statusMatch && assigneeMatch) {
          result.push(node);
        }
      }
    });
    return result;
  };
  return filterNodes(filteredData);
});
// 树节点配置
const defaultProps = {
  children: 'children',
  label: (data) => {
    if (data.type === 'phase') {
      return `${data.phaseName}`;
    } else {
      return `${data.taskName}`;
    }
  }
};
// 加载任务树数据
const loadTaskTree = async () => {
  loading.value = true;
  // try {
  //   const { data } = await getProject(props.projectId);
  //   rawTaskTreeData.value = buildTaskTree(data.phases || []);
  // } catch (error) {
  //   ElMessage.error('加载任务树失败');
  //   console.error('加载任务树失败:', error);
  // } finally {
  //   loading.value = false;
  // }
  try {
    // 模拟网络延迟
    await new Promise(resolve => setTimeout(resolve, 500));
    // 使用模拟数据替代API请求
    const phases = mockTaskData[props.projectId] || mockTaskData.default;
    rawTaskTreeData.value = buildTaskTree(phases);
  } catch (error) {
    ElMessage.error('加载任务树失败');
    console.error('加载任务树失败:', error);
  } finally {
    loading.value = false;
  }
};
// 构建任务树
const buildTaskTree = (phases) => {
  return phases.map(phase => ({
    nodeId: phase.phaseId,
    phaseId: phase.phaseId,
    phaseName: phase.phaseName,
    type: 'phase',
    children: (phase.tasks || []).map(task => ({
      nodeId: task.taskId,
      taskId: task.taskId,
      taskName: task.taskName,
      description: task.description,
      startDate: task.startDate,
      endDate: task.endDate,
      assigneeId: task.assigneeId,
      assigneeName: task.assigneeName,
      status: task.status,
      progress: task.progress,
      priority: task.priority,
      phaseId: task.phaseId,
      projectId: props.projectId,
      type: 'task'
    }))
  }));
};
// 格式化日期范围
const formatDateRange = (startDate, endDate) => {
  if (!startDate || !endDate) return '';
  return `${startDate} - ${endDate}`;
};
// 刷新树
const refreshTree = () => {
  loadTaskTree();
  // 通知父组件刷新数据
  emit('refresh');
};
// 切换筛选面板
const toggleFilter = () => {
  showFilter.value = !showFilter.value;
};
// 应用筛选
const filterTree = () => {
  // 筛选逻辑已经在computed中实现
};
// 重置筛选
const resetFilter = () => {
  filterParams.status = '';
  filterParams.assignee = '';
};
// 处理节点点击
const handleNodeClick = (data, node) => {
  // 切换展开/收起状态
  if (data.type === 'phase') {
    node.expanded = !node.expanded;
  }
};
// 处理右键菜单
const handleContextMenu = (event, data) => {
  event.preventDefault();
  selectedNode.value = data;
  contextMenuStyle.value = {
    position: 'fixed',
    left: `${event.clientX}px`,
    top: `${event.clientY}px`,
    zIndex: 1000
  };
  showContextMenu.value = true;
};
// 处理右键菜单选择
const handleContextMenuSelect = (index) => {
  showContextMenu.value = false;
  switch (index) {
    case 'edit':
      if (selectedNode.value.type === 'task') {
        handleEditTask(selectedNode.value);
      }
      break;
    case 'addTask':
      if (selectedNode.value.type === 'phase') {
        handleAddTaskUnderPhase(selectedNode.value);
      }
      break;
    case 'delete':
      handleDeleteNode(selectedNode.value);
      break;
    case 'expandAll':
      treeRef.value?.expandAll();
      break;
    case 'collapseAll':
      treeRef.value?.collapseAll();
      break;
  }
};
// 添加任务
const handleAddTask = () => {
  resetTaskForm();
  dialogTitle.value = '添加任务';
  dialogOpen.value = true;
};
// 在指定阶段下添加任务
const handleAddTaskUnderPhase = (phase) => {
  resetTaskForm();
  taskForm.phaseId = phase.phaseId;
  dialogTitle.value = '添加子任务';
  dialogOpen.value = true;
};
// 编辑任务
const handleEditTask = (task) => {
  resetTaskForm();
  Object.assign(taskForm, { ...task });
  dialogTitle.value = '编辑任务';
  dialogOpen.value = true;
};
// 删除节点
const handleDeleteNode = async (node) => {
  const confirmMessage = node.type === 'phase'
    ? `确定要删除阶段 "${node.phaseName}" 及其所有子任务吗?`
    : `确定要删除任务 "${node.taskName}" 吗?`;
  await ElMessageBox.confirm(confirmMessage, '确认操作', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).catch(() => {
    throw new Error('取消删除');
  });
  try {
    if (node.type === 'phase') {
      await deletePhase(node.phaseId);
    } else {
      await deleteTask(node.taskId);
  const emit = defineEmits(["refresh"]);
  // 组件状态
  const loading = ref(false);
  const treeRef = ref();
  const showContextMenu = ref(false);
  const contextMenuStyle = ref({});
  const selectedNode = ref({});
  const showFilter = ref(false);
  const dialogOpen = ref(false);
  const dialogTitle = ref("");
  const taskFormRef = ref();
  // 筛选参数
  const filterParams = reactive({
    status: "",
    assignee: "",
  });
  // 任务表单数据
  const taskForm = reactive({
    taskId: undefined,
    taskName: "",
    description: "",
    startDate: "",
    endDate: "",
    assigneeId: "",
    assigneeName: "",
    status: "notStarted",
    progress: 0,
    priority: "medium",
    phaseId: "",
    projectId: props.projectId,
  });
  // 表单验证规则
  const taskRules = {
    taskName: [
      { required: true, message: "任务名称不能为空", trigger: "blur" },
      {
        min: 2,
        max: 50,
        message: "任务名称长度在 2 到 50 个字符",
        trigger: "blur",
      },
    ],
    startDate: [
      { required: true, message: "开始日期不能为空", trigger: "change" },
    ],
    endDate: [{ required: true, message: "结束日期不能为空", trigger: "change" }],
    assigneeId: [{ required: true, message: "负责人不能为空", trigger: "blur" }],
    progress: [
      { required: true, message: "进度不能为空", trigger: "blur" },
      {
        type: "number",
        min: 0,
        max: 100,
        message: "进度必须在 0 到 100 之间",
        trigger: "blur",
      },
    ],
  };
  // 任务树数据
  const rawTaskTreeData = ref([]);
  // 模拟任务数据
  const mockTaskData = {
    PRJ2023001: [
      {
        phaseId: "PHASE001",
        phaseName: "需求分析",
        startDate: "2023-11-01",
        endDate: "2023-11-15",
        status: "completed",
        tasks: [
          {
            taskId: "TASK001",
            taskName: "需求调研",
            description: "调研用户需求和业务流程",
            startDate: "2023-11-01",
            endDate: "2023-11-05",
            assigneeId: "USER001",
            assigneeName: "张三",
            status: "completed",
            progress: 100,
            priority: "medium",
          },
          {
            taskId: "TASK002",
            taskName: "需求文档编写",
            description: "编写详细的需求规格说明书",
            startDate: "2023-11-06",
            endDate: "2023-11-15",
            assigneeId: "USER002",
            assigneeName: "李四",
            status: "completed",
            progress: 100,
            priority: "high",
          },
        ],
      },
      {
        phaseId: "PHASE002",
        phaseName: "系统设计",
        startDate: "2023-11-16",
        endDate: "2023-12-10",
        status: "completed",
        tasks: [
          {
            taskId: "TASK003",
            taskName: "系统架构设计",
            description: "设计系统整体架构",
            startDate: "2023-11-16",
            endDate: "2023-11-25",
            assigneeId: "USER003",
            assigneeName: "王五",
            status: "completed",
            progress: 100,
            priority: "high",
          },
          {
            taskId: "TASK004",
            taskName: "数据库设计",
            description: "设计数据库表结构和关系",
            startDate: "2023-11-26",
            endDate: "2023-12-10",
            assigneeId: "USER004",
            assigneeName: "赵六",
            status: "completed",
            progress: 100,
            priority: "medium",
          },
        ],
      },
      {
        phaseId: "PHASE003",
        phaseName: "开发实现",
        startDate: "2023-12-11",
        endDate: "2024-01-31",
        status: "inProgress",
        tasks: [
          {
            taskId: "TASK005",
            taskName: "前端开发",
            description: "开发用户界面和交互逻辑",
            startDate: "2023-12-11",
            endDate: "2024-01-15",
            assigneeId: "USER005",
            assigneeName: "钱七",
            status: "inProgress",
            progress: 70,
            priority: "high",
          },
          {
            taskId: "TASK006",
            taskName: "后端开发",
            description: "开发业务逻辑和API接口",
            startDate: "2023-12-11",
            endDate: "2024-01-20",
            assigneeId: "USER006",
            assigneeName: "孙八",
            status: "inProgress",
            progress: 60,
            priority: "high",
          },
        ],
      },
    ],
    // 默认数据
    default: [
      {
        phaseId: "PHASE_DEFAULT1",
        phaseName: "准备阶段",
        startDate: "2023-01-01",
        endDate: "2023-03-31",
        status: "completed",
        tasks: [
          {
            taskId: "TASK_DEFAULT1",
            taskName: "项目启动",
            description: "召开项目启动会议",
            startDate: "2023-01-01",
            endDate: "2023-01-05",
            assigneeId: "USER_DEFAULT1",
            assigneeName: "负责人A",
            status: "completed",
            progress: 100,
            priority: "high",
          },
        ],
      },
      {
        phaseId: "PHASE_DEFAULT2",
        phaseName: "执行阶段",
        startDate: "2023-04-01",
        endDate: "2023-09-30",
        status: "inProgress",
        tasks: [
          {
            taskId: "TASK_DEFAULT2",
            taskName: "核心功能开发",
            description: "开发系统核心功能模块",
            startDate: "2023-04-01",
            endDate: "2023-06-30",
            assigneeId: "USER_DEFAULT2",
            assigneeName: "负责人B",
            status: "inProgress",
            progress: 50,
            priority: "high",
          },
        ],
      },
    ],
  };
  const taskTreeData = computed(() => {
    // 应用筛选条件
    if (!showFilter.value || (!filterParams.status && !filterParams.assignee)) {
      return rawTaskTreeData.value;
    }
    ElMessage.success('删除成功');
    refreshTree();
  } catch (error) {
    if (error.message !== '取消删除') {
      ElMessage.error('删除失败');
      console.error('删除失败:', error);
    // 深拷贝原始数据以避免修改
    const filteredData = JSON.parse(JSON.stringify(rawTaskTreeData.value));
    // 递归筛选节点
    const filterNodes = nodes => {
      const result = [];
      nodes.forEach(node => {
        // 对于阶段节点,检查其子任务是否符合筛选条件
        if (node.type === "phase" && node.children) {
          const filteredChildren = filterNodes(node.children);
          if (filteredChildren.length > 0) {
            // 保留至少有一个子任务符合条件的阶段
            node.children = filteredChildren;
            result.push(node);
          }
        }
        // 对于任务节点,直接应用筛选条件
        else if (node.type === "task") {
          const statusMatch =
            !filterParams.status || node.status === filterParams.status;
          const assigneeMatch =
            !filterParams.assignee ||
            (node.assigneeName &&
              node.assigneeName.includes(filterParams.assignee));
          if (statusMatch && assigneeMatch) {
            result.push(node);
          }
        }
      });
      return result;
    };
    return filterNodes(filteredData);
  });
  // 树节点配置
  const defaultProps = {
    children: "children",
    label: data => {
      if (data.type === "phase") {
        return `${data.phaseName}`;
      } else {
        return `${data.taskName}`;
      }
    },
  };
  // 加载任务树数据
  const loadTaskTree = async () => {
    loading.value = true;
    // try {
    //   const { data } = await getProject(props.projectId);
    //   rawTaskTreeData.value = buildTaskTree(data.phases || []);
    // } catch (error) {
    //   ElMessage.error('加载任务树失败');
    //   console.error('加载任务树失败:', error);
    // } finally {
    //   loading.value = false;
    // }
    try {
      // 模拟网络延迟
      await new Promise(resolve => setTimeout(resolve, 500));
      // 使用模拟数据替代API请求
      const phases = mockTaskData[props.projectId] || mockTaskData.default;
      rawTaskTreeData.value = buildTaskTree(phases);
    } catch (error) {
      ElMessage.error("加载任务树失败");
      console.error("加载任务树失败:", error);
    } finally {
      loading.value = false;
    }
  }
};
  };
// 重置任务表单
const resetTaskForm = () => {
  taskForm.taskId = undefined;
  taskForm.taskName = '';
  taskForm.description = '';
  taskForm.startDate = '';
  taskForm.endDate = '';
  taskForm.assigneeId = '';
  taskForm.assigneeName = '';
  taskForm.status = 'notStarted';
  taskForm.progress = 0;
  taskForm.priority = 'medium';
  taskForm.phaseId = '';
  taskForm.projectId = props.projectId;
  if (taskFormRef.value) {
    taskFormRef.value.resetFields();
  }
};
  // 构建任务树
  const buildTaskTree = phases => {
    return phases.map(phase => ({
      nodeId: phase.phaseId,
      phaseId: phase.phaseId,
      phaseName: phase.phaseName,
      type: "phase",
      children: (phase.tasks || []).map(task => ({
        nodeId: task.taskId,
        taskId: task.taskId,
        taskName: task.taskName,
        description: task.description,
        startDate: task.startDate,
        endDate: task.endDate,
        assigneeId: task.assigneeId,
        assigneeName: task.assigneeName,
        status: task.status,
        progress: task.progress,
        priority: task.priority,
        phaseId: task.phaseId,
        projectId: props.projectId,
        type: "task",
      })),
    }));
  };
// 提交任务表单
const submitTaskForm = async () => {
  try {
    await taskFormRef.value.validate();
    if (taskForm.taskId) {
      await updateTask(taskForm);
      ElMessage.success('修改任务成功');
    } else {
      await addTask(taskForm);
      ElMessage.success('添加任务成功');
    }
    dialogOpen.value = false;
    refreshTree();
  } catch (error) {
    console.error('提交表单失败:', error);
  }
};
  // 格式化日期范围
  const formatDateRange = (startDate, endDate) => {
    if (!startDate || !endDate) return "";
    return `${startDate} - ${endDate}`;
  };
// 点击其他区域关闭右键菜单
document.addEventListener('click', () => {
  if (showContextMenu.value) {
    showContextMenu.value = false;
  }
});
// 监听项目ID变化
watch(() => props.projectId, (newProjectId) => {
  if (newProjectId) {
  // 刷新树
  const refreshTree = () => {
    loadTaskTree();
  }
});
    // 通知父组件刷新数据
    emit("refresh");
  };
// 初始化
onMounted(() => {
  loadTaskTree();
});
  // 切换筛选面板
  const toggleFilter = () => {
    showFilter.value = !showFilter.value;
  };
  // 应用筛选
  const filterTree = () => {
    // 筛选逻辑已经在computed中实现
  };
  // 重置筛选
  const resetFilter = () => {
    filterParams.status = "";
    filterParams.assignee = "";
  };
  // 处理节点点击
  const handleNodeClick = (data, node) => {
    // 切换展开/收起状态
    if (data.type === "phase") {
      node.expanded = !node.expanded;
    }
  };
  // 处理右键菜单
  const handleContextMenu = (event, data) => {
    event.preventDefault();
    selectedNode.value = data;
    contextMenuStyle.value = {
      position: "fixed",
      left: `${event.clientX}px`,
      top: `${event.clientY}px`,
      zIndex: 1000,
    };
    showContextMenu.value = true;
  };
  // 处理右键菜单选择
  const handleContextMenuSelect = index => {
    showContextMenu.value = false;
    switch (index) {
      case "edit":
        if (selectedNode.value.type === "task") {
          handleEditTask(selectedNode.value);
        }
        break;
      case "addTask":
        if (selectedNode.value.type === "phase") {
          handleAddTaskUnderPhase(selectedNode.value);
        }
        break;
      case "delete":
        handleDeleteNode(selectedNode.value);
        break;
      case "expandAll":
        treeRef.value?.expandAll();
        break;
      case "collapseAll":
        treeRef.value?.collapseAll();
        break;
    }
  };
  // 添加任务
  const handleAddTask = () => {
    resetTaskForm();
    dialogTitle.value = "添加任务";
    dialogOpen.value = true;
  };
  // 在指定阶段下添加任务
  const handleAddTaskUnderPhase = phase => {
    resetTaskForm();
    taskForm.phaseId = phase.phaseId;
    dialogTitle.value = "添加子任务";
    dialogOpen.value = true;
  };
  // 编辑任务
  const handleEditTask = task => {
    resetTaskForm();
    Object.assign(taskForm, { ...task });
    dialogTitle.value = "编辑任务";
    dialogOpen.value = true;
  };
  // 删除节点
  const handleDeleteNode = async node => {
    const confirmMessage =
      node.type === "phase"
        ? `确定要删除阶段 "${node.phaseName}" 及其所有子任务吗?`
        : `确定要删除任务 "${node.taskName}" 吗?`;
    await ElMessageBox.confirm(confirmMessage, "确认操作", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).catch(() => {
      throw new Error("取消删除");
    });
    try {
      if (node.type === "phase") {
        await deletePhase(node.phaseId);
      } else {
        await deleteTask(node.taskId);
      }
      ElMessage.success("删除成功");
      refreshTree();
    } catch (error) {
      if (error.message !== "取消删除") {
        ElMessage.error("删除失败");
        console.error("删除失败:", error);
      }
    }
  };
  // 重置任务表单
  const resetTaskForm = () => {
    taskForm.taskId = undefined;
    taskForm.taskName = "";
    taskForm.description = "";
    taskForm.startDate = "";
    taskForm.endDate = "";
    taskForm.assigneeId = "";
    taskForm.assigneeName = "";
    taskForm.status = "notStarted";
    taskForm.progress = 0;
    taskForm.priority = "medium";
    taskForm.phaseId = "";
    taskForm.projectId = props.projectId;
    if (taskFormRef.value) {
      taskFormRef.value.resetFields();
    }
  };
  // 提交任务表单
  const submitTaskForm = async () => {
    try {
      await taskFormRef.value.validate();
      if (taskForm.taskId) {
        await updateTask(taskForm);
        ElMessage.success("修改任务成功");
      } else {
        await addTask(taskForm);
        ElMessage.success("添加任务成功");
      }
      dialogOpen.value = false;
      refreshTree();
    } catch (error) {
      console.error("提交表单失败:", error);
    }
  };
  // 点击其他区域关闭右键菜单
  document.addEventListener("click", () => {
    if (showContextMenu.value) {
      showContextMenu.value = false;
    }
  });
  // 监听项目ID变化
  watch(
    () => props.projectId,
    newProjectId => {
      if (newProjectId) {
        loadTaskTree();
      }
    }
  );
  // 初始化
  onMounted(() => {
    loadTaskTree();
  });
</script>
<style scoped>
.task-tree-container {
  padding: 10px;
}
  .task-tree-container {
    padding: 10px;
  }
.tree-actions {
  display: flex;
  gap: 10px;
  align-items: center;
}
  .tree-actions {
    display: flex;
    gap: 10px;
    align-items: center;
  }
.filter-section {
  background: #f5f7fa;
  padding: 10px;
  border-radius: 4px;
}
  .filter-section {
    background: #f5f7fa;
    padding: 10px;
    border-radius: 4px;
  }
.tree-content {
  background: #fff;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  padding: 10px;
  max-height: 600px;
  overflow-y: auto;
}
  .tree-content {
    background: #fff;
    border: 1px solid #ebeef5;
    border-radius: 4px;
    padding: 10px;
    max-height: 600px;
    overflow-y: auto;
  }
.tree-node-content {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  min-height: 40px;
}
  .tree-node-content {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 0;
    min-height: 40px;
  }
.phase-node {
  font-weight: bold;
  color: #409eff;
}
  .phase-node {
    font-weight: bold;
    color: #409eff;
  }
.task-node {
  color: #606266;
}
  .task-node {
    color: #606266;
  }
.node-icon {
  display: flex;
  align-items: center;
  width: 20px;
}
  .node-icon {
    display: flex;
    align-items: center;
    width: 20px;
  }
.node-info {
  flex: 1;
  min-width: 0;
}
  .node-info {
    flex: 1;
    min-width: 0;
  }
.node-title {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 2px;
}
  .node-title {
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-bottom: 2px;
  }
.overdue-title {
  color: #f56c6c;
  font-weight: bold;
}
  .overdue-title {
    color: #f56c6c;
    font-weight: bold;
  }
.priority-tag {
  background: #f56c6c;
  color: white;
  font-size: 10px;
  padding: 1px 4px;
  border-radius: 2px;
  margin-left: 5px;
}
  .priority-tag {
    background: #f56c6c;
    color: white;
    font-size: 10px;
    padding: 1px 4px;
    border-radius: 2px;
    margin-left: 5px;
  }
.priority-tag.medium {
  background: #e6a23c;
}
  .priority-tag.medium {
    background: #e6a23c;
  }
.node-description {
  font-size: 12px;
  color: #909399;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
  .node-description {
    font-size: 12px;
    color: #909399;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
.task-meta {
  display: flex;
  gap: 15px;
  font-size: 12px;
  color: #909399;
  margin-top: 2px;
}
  .task-meta {
    display: flex;
    gap: 15px;
    font-size: 12px;
    color: #909399;
    margin-top: 2px;
  }
.meta-item {
  display: flex;
  align-items: center;
  gap: 3px;
}
  .meta-item {
    display: flex;
    align-items: center;
    gap: 3px;
  }
.task-progress {
  width: 120px;
  margin: 0 10px;
}
  .task-progress {
    width: 120px;
    margin: 0 10px;
  }
.node-actions {
  display: flex;
  gap: 5px;
  opacity: 0;
  transition: opacity 0.3s;
}
  .node-actions {
    display: flex;
    gap: 5px;
    opacity: 0;
    transition: opacity 0.3s;
  }
.tree-node-content:hover .node-actions {
  opacity: 1;
}
  .tree-node-content:hover .node-actions {
    opacity: 1;
  }
.context-menu {
  background: white;
  border: 1px solid #ebeef5;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  border-radius: 4px;
}
  .context-menu {
    background: white;
    border: 1px solid #ebeef5;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    border-radius: 4px;
  }
.context-menu .el-menu {
  min-width: 120px;
  border: none;
}
  .context-menu .el-menu {
    min-width: 120px;
    border: none;
  }
.context-menu .el-menu-item {
  padding: 0 15px;
  height: 36px;
  line-height: 36px;
}
  .context-menu .el-menu-item {
    padding: 0 15px;
    height: 36px;
    line-height: 36px;
  }
.context-menu .el-menu-item:hover {
  background-color: #f5f7fa;
}
  .context-menu .el-menu-item:hover {
    background-color: #f5f7fa;
  }
.text-gray-400 {
  color: #c0c4cc;
}
  .text-gray-400 {
    color: #c0c4cc;
  }
</style>
在上述文件截断后对比
src/views/oaSystem/projectManagement/index.vue src/views/oaSystem/projectManagement/projectDetail.vue src/views/procurementManagement/procurementPlan/index.vue src/views/productManagement/productIdentifier/index.vue src/views/productionManagement/productStructure/StructureEdit.vue src/views/productionManagement/workOrder/index.vue src/views/salesManagement/customerManagement/index.vue src/views/salesManagement/orderManagement/index.vue src/views/salesManagement/paymentShipping/index.vue src/views/salesManagement/salesQuotation/index.vue src/views/salesManagement/salespersonManagement/index.vue src/views/tool/gen/importTable.vue