spring
9 天以前 465c8711c348e2a33800a31157131ab4640a5668
行政管理
已添加1个文件
1187 ■■■■■ 文件已修改
src/views/collaborativeApproval/notificationManagement/index.vue 1187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/notificationManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1187 @@
<template>
  <div class="app-container">
    <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"
        />
        <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>
        <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="info" @click="refreshEmployees">刷新员工</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>
    </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-row :gutter="20">
          <el-col :span="12">
            <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-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-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="请选择有效期"
                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-select>
        </el-form-item>
        <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"
            >
              {{ 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>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <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-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"
                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%"
              />
              <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-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-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;"
            >
              {{ 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>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <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-form-item>
        <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-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-button type="primary">选择文件</el-button>
            <template #tip>
              <div class="el-upload__tip">
                æ”¯æŒä¸Šä¼ æ–‡æ¡£ã€å›¾ç‰‡ç­‰æ ¼å¼ï¼Œå•个文件不超过10MB,最多10个文件
              </div>
            </template>
          </el-upload>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="fileShareDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="shareFiles">共享文件</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</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";
// è¡¨å•验证规则
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: 100,
    total: 0,
  },
  tableData: [],
  selectedIds: [],
  // æ–°å¢žé€šçŸ¥ç›¸å…³
  form: {
    title: "",
    type: "",
    priority: "medium",
    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"
      }
    ]
  }
]);
// æ¨¡æ‹Ÿæ•°æ®
let mockData = [
  {
    id: "1",
    title: "2024年春节放假通知",
    type: "holiday",
    priority: "high",
    status: "published",
    content: "根据国家规定,结合公司实际情况,现将2024年春节放假安排通知如下...",
    departments: ["技术部", "销售部", "人事部", "财务部", "运营部", "市场部", "客服部"],
    expireDate: "2024-02-15",
    syncMethods: ["wechat", "dingtalk", "email"],
    createTime: "2024-01-15 10:30:00"
  },
  {
    id: "2",
    title: "技术部周例会通知",
    type: "meeting",
    priority: "medium",
    status: "published",
    content: "技术部定于每周五下午2点召开周例会,请各位同事准时参加...",
    departments: ["技术部"],
    expireDate: "2024-01-20",
    syncMethods: ["wechat", "dingtalk"],
    createTime: "2024-01-14 15:20:00"
  },
  {
    id: "3",
    title: "员工行为规范处罚通知",
    type: "penalty",
    priority: "high",
    status: "draft",
    content: "为维护公司正常秩序,规范员工行为,现对违反公司规定的行为进行处罚...",
    departments: ["人事部", "技术部", "销售部"],
    expireDate: "2024-02-13",
    syncMethods: ["wechat", "email"],
    createTime: "2024-01-13 09:15:00"
  }
];
// é€šçŸ¥æ ‡é¢˜æ¨¡æ¿
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()
  };
  // æ·»åŠ åˆ°æ•°æ®å¼€å¤´
  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;
  setTimeout(() => {
    let filteredData = [...mockData];
    if (searchForm.value.title) {
      filteredData = filteredData.filter(item =>
        item.title.toLowerCase().includes(searchForm.value.title.toLowerCase())
      );
    }
    if (searchForm.value.type) {
      filteredData = filteredData.filter(item => item.type === searchForm.value.type);
    }
    tableData.value = filteredData;
    page.value.total = filteredData.length;
    tableLoading.value = false;
  }, 500);
};
// åˆ†é¡µå¤„理
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: "",
      priority: "medium",
      content: "",
      departments: [],
      expireDate: "",
      syncMethods: []
    });
  } else if (type === "edit" && row) {
    dialogTitle.value = "编辑通知";
    Object.assign(form.value, {
      title: row.title,
      type: row.type,
      priority: row.priority,
      content: row.content || "",
      departments: row.departments || [],
      expireDate: row.expireDate || "",
      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") {
      // æ–°å¢žé€šçŸ¥
      const newNotification = {
        id: (mockData.length + 1).toString(),
        title: form.value.title,
        type: form.value.type,
        priority: form.value.priority,
        status: "draft",
        content: form.value.content,
        departments: form.value.departments,
        expireDate: form.value.expireDate,
        syncMethods: form.value.syncMethods,
        createTime: new Date().toLocaleString()
      };
      mockData.unshift(newNotification);
      ElMessage.success("通知创建成功");
    } else {
      // ç¼–辑通知
      const index = mockData.findIndex(item => item.id === selectedIds.value[0]);
      if (index !== -1) {
        Object.assign(mockData[index], {
          title: form.value.title,
          type: form.value.type,
          priority: form.value.priority,
          content: form.value.content,
          departments: form.value.departments,
          expireDate: form.value.expireDate,
          syncMethods: form.value.syncMethods
        });
        ElMessage.success("通知更新成功");
      }
    }
    dialogVisible.value = false;
    getList();
  } 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,
      meetingId: `MTG${Date.now()}`
    };
    // æ¨¡æ‹Ÿå‘送到企业微信/钉钉
    const platformName = meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "未知平台";
    ElMessage.success(`会议创建成功!会议ID: ${meetingInfo.meetingId},将通过${platformName}发送通知`);
    meetingDialogVisible.value = false;
         // èŽ·å–å‚ä¼šäººå‘˜ä¿¡æ¯
     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 = {
      id: (mockData.length + 1).toString(),
      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],
      createTime: new Date().toLocaleString()
    };
    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);
  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,
      shareId: `FILE${Date.now()}`
    };
    ElMessage.success(`文件共享成功!共享ID: ${shareInfo.shareId},已通知相关部门`);
    fileShareDialogVisible.value = false;
    // å°†æ–‡ä»¶å…±äº«ä¿¡æ¯æ·»åŠ åˆ°é€šçŸ¥åˆ—è¡¨
    const fileShareNotification = {
      id: (mockData.length + 1).toString(),
      title: `[文件共享] ${shareInfo.title}`,
      type: "temporary",
      priority: "medium",
      status: "published",
      content: `共享描述: ${shareInfo.description},文件数量: ${shareInfo.files.length}个`,
      departments: shareInfo.departments,
      expireDate: "",
      syncMethods: ["wechat", "dingtalk"],
      createTime: new Date().toLocaleString()
    };
    mockData.unshift(fileShareNotification);
    getList();
  } catch (error) {
    console.error("文件共享表单验证失败:", error);
  }
};
// å‘布通知
const publishNotification = (row) => {
  row.status = "published";
  ElMessage.success("通知发布成功");
  getList();
};
// æ’¤å›žé€šçŸ¥
const revokeNotification = (row) => {
  row.status = "draft";
  ElMessage.success("通知已撤回");
  getList();
};
// åˆ é™¤é€šçŸ¥
const handleDelete = () => {
  if (selectedIds.value.length === 0) {
    ElMessage.warning("请选择要删除的通知");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    ElMessage.success("删除成功");
    selectedIds.value = [];
    getList();
  }).catch(() => {
    // ç”¨æˆ·å–消
  });
};
</script>
<style scoped>
.auto-refresh-info {
  margin-bottom: 15px;
}
.auto-refresh-info .el-alert {
  border-radius: 8px;
}
.dialog-footer {
  text-align: right;
}
.el-upload__tip {
  color: #909399;
  font-size: 12px;
  margin-top: 8px;
}
.el-checkbox-group {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.el-checkbox {
  margin-right: 0;
}
</style>