WXL
13 小时以前 0c03027d7f238bf5beb98e85463f53f0bd92bbaa
src/views/OfficeRelated/conference/index.vue
@@ -2,7 +2,6 @@
  <div class="meeting-management">
    <!-- 页面头部 -->
    <div class="page-header">
      <h2>会议管理</h2>
      <div class="header-actions">
        <el-button type="primary" icon="el-icon-plus" @click="handleAdd">
          新建会议
@@ -17,7 +16,11 @@
    <el-card class="filter-card">
      <el-form :model="queryParams" inline>
        <el-form-item label="会议类型">
          <el-select v-model="queryParams.meetingType" clearable placeholder="请选择">
          <el-select
            v-model="queryParams.meetingType"
            clearable
            placeholder="请选择"
          >
            <el-option label="科研会议" value="research" />
            <el-option label="日常会议" value="daily" />
            <el-option label="项目会议" value="project" />
@@ -33,7 +36,7 @@
            style="width: 150px"
          />
        </el-form-item>
        <el-form-item label="时间范围">
        <el-form-item label="会议开始时间范围">
          <el-date-picker
            v-model="queryParams.dateRange"
            type="daterange"
@@ -44,12 +47,27 @@
          />
        </el-form-item>
        <el-form-item label="状态">
          <el-select v-model="queryParams.status" clearable placeholder="请选择">
            <el-option label="待开始" value="pending" />
            <el-option label="进行中" value="ongoing" />
            <el-option label="已结束" value="completed" />
            <el-option label="已取消" value="cancelled" />
          <el-select
            v-model="queryParams.status"
            clearable
            placeholder="请选择"
          >
            <el-option label="待开始" value="1" />
            <el-option label="进行中" value="2" />
            <el-option label="已结束" value="3" />
            <el-option label="已取消" value="4" />
            <el-option label="待审核" value="0" />
            <el-option label="已通过" value="1" />
            <el-option label="已驳回" value="2" />
          </el-select>
        </el-form-item>
        <el-form-item label="会议主题">
          <el-input
            v-model="queryParams.title"
            placeholder="请输入会议主题"
            clearable
            style="width: 150px"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery">查询</el-button>
@@ -67,63 +85,140 @@
        style="width: 100%"
        @sort-change="handleSortChange"
      >
        <el-table-column prop="id" label="ID" width="80" fixed />
        <el-table-column prop="title" label="会议主题" width="200" fixed>
        <el-table-column prop="id" label="ID" width="80" fixed align="center" />
        <el-table-column
          prop="title"
          align="center"
          label="会议主题"
          width="200"
          fixed
        >
          <template #default="scope">
            <el-button type="text" @click="handleView(scope.row)">
            <el-button type="text" @click="handleView(scope.row.id)">
              {{ scope.row.title }}
            </el-button>
          </template>
        </el-table-column>
        <el-table-column prop="meetingType" label="会议类型" width="120">
        <el-table-column
          align="center"
          prop="meetingType"
          label="会议类型"
          width="120"
        >
          <template #default="scope">
            <el-tag :type="getMeetingTypeTag(scope.row.meetingType)">
              {{ getMeetingTypeText(scope.row.meetingType) }}
            <el-tag :type="getMeetingTypeTag(scope.row.typeId)">
              {{ getMeetingTypeText(scope.row.typeId) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="participants" label="参会人员" width="180" show-overflow-tooltip>
        <el-table-column
          align="center"
          label="会议状态"
          width="100"
          fixed="right"
        >
          <template #default="scope">
            <span>{{ scope.row.participants.join(', ') }}</span>
            <el-tag :type="getMeetingStatusTag(scope.row.status)">
              {{ getMeetingStatusText(scope.row.status) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="location" label="会议地点" width="150" />
        <el-table-column prop="startTime" label="开始时间" width="160" sortable>
        <el-table-column
          align="center"
          prop="organizerName"
          label="组织者"
          width="120"
          ><template #default="scope">
            <span>{{ scope.organizerName || scope.createBy }}</span>
          </template>
        </el-table-column>
        <el-table-column
          align="center"
          prop="locationName"
          label="会议地点"
          width="150"
        />
        <el-table-column
          align="center"
          prop="startTime"
          label="开始时间"
          width="160"
          sortable
        >
          <template #default="scope">
            <span>{{ formatDateTime(scope.row.startTime) }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="endTime" label="结束时间" width="160" sortable>
        <el-table-column
          align="center"
          prop="endTime"
          label="结束时间"
          width="160"
          sortable
        >
          <template #default="scope">
            <span>{{ formatDateTime(scope.row.endTime) }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="duration" label="持续时间" width="100">
        <el-table-column
          align="center"
          prop="duration"
          label="持续时间"
          width="100"
        >
          <template #default="scope">
            <span>{{ calculateDuration(scope.row) }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="summary" label="会议概要" min-width="200" show-overflow-tooltip />
        <el-table-column prop="status" label="状态" width="100" fixed="right">
        <el-table-column
          align="center"
          prop="summary"
          label="会议概要"
          min-width="200"
          show-overflow-tooltip
        />
        <el-table-column align="center" label="会议纪要" width="120">
          <template #default="scope">
            <el-tag :type="getStatusTag(scope.row.status)">
              {{ getStatusText(scope.row.status) }}
            <el-button
              size="mini"
              type="text"
              @click="handleViewMinutes(scope.row)"
              :disabled="!scope.row.recordcontent"
            >
              {{ scope.row.recordcontent ? "查看纪要" : "暂无" }}
            </el-button>
          </template>
        </el-table-column>
        <el-table-column align="center" label="审核状态" width="100">
          <template #default="scope">
            <el-tag :type="getApprovalStatusTag(scope.row.approvalStatus)">
              {{ getApprovalStatusText(scope.row.approvalStatus) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="200" fixed="right">
        <el-table-column label="操作" align="center" width="200" fixed="right">
          <template #default="scope">
            <el-button size="mini" type="text" @click="handleView(scope.row)">
            <el-button
              size="mini"
              type="text"
              @click="handleView(scope.row.id)"
            >
              查看
            </el-button>
            <el-button size="mini" type="text" @click="handleEdit(scope.row)">
            <el-button
              size="mini"
              type="text"
              @click="handleEdit(scope.row.id)"
              :disabled="scope.row.delFlag === 1"
            >
              编辑
            </el-button>
            <el-button
              size="mini"
              type="text"
              @click="handleCopy(scope.row)"
              @click="handleCopy(scope.row.id)"
              style="color: #67C23A;"
              :disabled="scope.row.delFlag === 1"
            >
              复制
            </el-button>
@@ -132,6 +227,7 @@
              type="text"
              @click="handleDelete(scope.row)"
              style="color: #F56C6C;"
              :disabled="scope.row.delFlag === 1"
            >
              删除
            </el-button>
@@ -142,15 +238,163 @@
      <!-- 分页 -->
      <div class="pagination-container">
        <el-pagination
          :current-page="pagination.currentPage"
          :current-page="pagination.pageNum"
          :page-size="pagination.pageSize"
          :total="pagination.total"
          layout="total, sizes, prev, pager, next, jumper"
          :page-sizes="[10, 20, 50, 100]"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
      </div>
    </el-card>
    <!-- 会议纪要对话框 -->
    <el-dialog
      title="会议纪要"
      :visible.sync="minutesDialogVisible"
      width="700px"
    >
      <div v-if="currentRecord.recordcontent">
        <el-alert
          title="会议纪要详情"
          type="info"
          :closable="false"
          style="margin-bottom: 16px;"
        />
        <div class="minutes-content">
          <el-card>
            <div
              style="white-space: pre-line; line-height: 1.6; max-height: 300px; overflow-y: auto;"
            >
              {{ currentRecord.recordcontent }}
            </div>
          </el-card>
        </div>
        <!-- 会议纪要附件展示 -->
        <div class="detail-attachments">
          <div
            v-if="parseAttachments(currentRecord.recordattachment).length > 0"
            class="attachment-grid"
          >
            <div
              v-for="file in parseAttachments(currentRecord.recordattachment)"
              :key="file.id || file.name"
              class="attachment-card"
            >
              <template v-if="isImageFile(file)">
                <!-- 图片使用 el-image 预览 -->
                <el-image
                  class="image-attachment"
                  :src="getFileUrl(file)"
                  :preview-src-list="getImagePreviewList(file)"
                  fit="cover"
                  :style="{ width: imageSize, height: imageSize }"
                  lazy
                >
                  <div slot="error" class="image-error">
                    <i class="el-icon-picture-outline"></i>
                    <span>加载失败</span>
                  </div>
                  <div slot="placeholder" class="image-loading">
                    <i class="el-icon-loading"></i>
                  </div>
                </el-image>
                <div class="image-info">
                  <div class="file-name" :title="file.name">
                    {{ file.name }}
                  </div>
                  <div class="file-actions">
                    <el-button
                      type="text"
                      size="mini"
                      @click="handleDownload(file)"
                      icon="el-icon-download"
                      title="下载"
                    />
                    <el-button
                      type="text"
                      size="mini"
                      @click="handlePreview(file)"
                      icon="el-icon-view"
                      title="预览"
                    />
                  </div>
                </div>
              </template>
              <template v-else>
                <!-- 非图片文件保持原有样式 -->
                <el-card shadow="hover" class="file-card">
                  <div class="file-content">
                    <i
                      :class="getFileIcon(file.type || file.name)"
                      class="file-icon"
                    ></i>
                    <div class="file-info">
                      <div class="file-name" :title="file.name">
                        {{ file.name }}
                      </div>
                      <div class="file-meta">
                        <span class="file-type">{{
                          getFileTypeText(file)
                        }}</span>
                        <span class="file-size" v-if="file.size">
                          {{ formatFileSize(file.size) }}
                        </span>
                      </div>
                    </div>
                  </div>
                  <div class="file-actions" v-if="file.url">
                    <el-button
                      type="text"
                      size="mini"
                      @click="handleDownload(file)"
                    >
                      下载
                    </el-button>
                    <el-button
                      v-if="canPreview(file)"
                      type="text"
                      size="mini"
                      @click="handlePreview(file)"
                    >
                      预览
                    </el-button>
                  </div>
                </el-card>
              </template>
            </div>
          </div>
        </div>
        <div
          class="minutes-meta"
          style="margin-top: 16px; color: #909399; font-size: 12px;"
        >
          <span
            >记录时间: {{ formatDateTime(currentRecord.recorderTime) }}</span
          >
          <span style="margin-left: 16px;"
            >记录人: {{ currentRecord.recorderBy || "未指定" }}</span
          >
        </div>
      </div>
      <div v-else>
        <el-empty description="暂无会议纪要"></el-empty>
      </div>
      <span slot="footer">
        <el-button @click="minutesDialogVisible = false">关闭</el-button>
        <el-button
          type="primary"
          @click="handleEdit(currentRecord.id)"
          v-if="currentRecord.recordcontent"
        >
          编辑纪要
        </el-button>
      </span>
    </el-dialog>
    <!-- 查看详情对话框 -->
    <el-dialog
@@ -159,53 +403,194 @@
      width="800px"
      :before-close="handleDetailClose"
    >
      <el-descriptions :column="2" border v-if="currentRecord">
        <el-descriptions-item label="会议主题">{{ currentRecord.title }}</el-descriptions-item>
      <el-descriptions :column="2" border v-if="currentRecord.id">
        <el-descriptions-item label="会议编号">{{
          currentRecord.meetingNumber
        }}</el-descriptions-item>
        <el-descriptions-item label="会议主题">{{
          currentRecord.title
        }}</el-descriptions-item>
        <el-descriptions-item label="会议类型">
          <el-tag :type="getMeetingTypeTag(currentRecord.meetingType)">
            {{ getMeetingTypeText(currentRecord.meetingType) }}
          <el-tag :type="getMeetingTypeTag(currentRecord.typeId)">
            {{ getMeetingTypeText(currentRecord.typeId) }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="参会人员" :span="2">
          <el-tag
            v-for="participant in currentRecord.participants"
            :key="participant"
            type="info"
            size="small"
            style="margin-right: 8px; margin-bottom: 8px;"
          >
            {{ participant }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="会议地点">{{ currentRecord.location }}</el-descriptions-item>
        <el-descriptions-item label="会议状态">
          <el-tag :type="getStatusTag(currentRecord.status)">
            {{ getStatusText(currentRecord.status) }}
          <el-tag :type="getMeetingStatusTag(currentRecord.status)">
            {{ getMeetingStatusText(currentRecord.status) }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="开始时间">{{ formatDateTime(currentRecord.startTime) }}</el-descriptions-item>
        <el-descriptions-item label="结束时间">{{ formatDateTime(currentRecord.endTime) }}</el-descriptions-item>
        <el-descriptions-item label="持续时间">{{ calculateDuration(currentRecord) }}</el-descriptions-item>
        <el-descriptions-item label="创建人">{{ currentRecord.creator }}</el-descriptions-item>
        <el-descriptions-item label="组织者">{{
          currentRecord.organizerName || currentRecord.createBy
        }}</el-descriptions-item>
        <el-descriptions-item label="会议地点">{{
          currentRecord.locationName || currentRecord.locationId
        }}</el-descriptions-item>
        <el-descriptions-item label="开始时间">{{
          formatDateTime(currentRecord.startTime)
        }}</el-descriptions-item>
        <el-descriptions-item label="结束时间">{{
          formatDateTime(currentRecord.endTime)
        }}</el-descriptions-item>
        <el-descriptions-item label="持续时间">{{
          calculateDuration(currentRecord)
        }}</el-descriptions-item>
        <el-descriptions-item label="审核状态">
          <el-tag :type="getApprovalStatusTag(currentRecord.approvalStatus)">
            {{ getApprovalStatusText(currentRecord.approvalStatus) }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="审核人">{{
          currentRecord.approverBy || "未审核"
        }}</el-descriptions-item>
        <el-descriptions-item label="审核时间">{{
          currentRecord.approvalTime
            ? formatDateTime(currentRecord.approvalTime)
            : "未审核"
        }}</el-descriptions-item>
        <el-descriptions-item label="会议概要" :span="2">
          {{ currentRecord.summary }}
        </el-descriptions-item>
        <el-descriptions-item label="会议内容" :span="2">
          <div style="white-space: pre-line; max-height: 300px; overflow-y: auto;">
            {{ currentRecord.content }}
          <div
            v-html="currentRecord.content"
            style="white-space: pre-line; max-height: 300px; overflow-y: auto;"
          ></div>
        </el-descriptions-item>
        <!-- 会议附件 -->
        我理解您的需求,您希望将会议附件中的图片文件以el-image的预览图形式展示,其他文件类型保持原有的展示方式。让我修改这部分代码:
        <el-descriptions-item label="会议附件" :span="2">
          <div class="detail-attachments">
            <div
              v-if="
                currentRecord.attachment &&
                  parseAttachments(currentRecord.attachment).length > 0
              "
              class="attachment-grid"
            >
              <!-- 图片附件 - 使用 el-image 预览形式 -->
              <div
                v-for="file in parseAttachments(currentRecord.attachment)"
                :key="file.id || file.name"
                class="attachment-card"
              >
                <template v-if="isImageFile(file)">
                  <!-- 图片使用 el-image 预览 -->
                  <el-image
                    class="image-attachment"
                    :src="getFileUrl(file)"
                    :preview-src-list="getImagePreviewList(file)"
                    fit="cover"
                    :style="{ width: imageSize, height: imageSize }"
                    lazy
                  >
                    <div slot="error" class="image-error">
                      <i class="el-icon-picture-outline"></i>
                      <span>加载失败</span>
                    </div>
                    <div slot="placeholder" class="image-loading">
                      <i class="el-icon-loading"></i>
                    </div>
                  </el-image>
                  <div class="image-info">
                    <div class="file-name" :title="file.name">
                      {{ file.name }}
                    </div>
                    <div class="file-actions">
                      <el-button
                        type="text"
                        size="mini"
                        @click="handleDownload(file)"
                        icon="el-icon-download"
                        title="下载"
                      />
                      <el-button
                        type="text"
                        size="mini"
                        @click="handlePreview(file)"
                        icon="el-icon-view"
                        title="预览"
                      />
                    </div>
                  </div>
                </template>
                <template v-else>
                  <!-- 非图片文件保持原有样式 -->
                  <el-card shadow="hover" class="file-card">
                    <div class="file-content">
                      <i
                        :class="getFileIcon(file.type || file.name)"
                        class="file-icon"
                      ></i>
                      <div class="file-info">
                        <div class="file-name" :title="file.name">
                          {{ file.name }}
                        </div>
                        <div class="file-meta">
                          <span class="file-type">{{
                            getFileTypeText(file)
                          }}</span>
                          <span class="file-size" v-if="file.size">
                            {{ formatFileSize(file.size) }}
                          </span>
                        </div>
                      </div>
                    </div>
                    <div class="file-actions" v-if="file.url">
                      <el-button
                        type="text"
                        size="mini"
                        @click="handleDownload(file)"
                      >
                        下载
                      </el-button>
                      <el-button
                        v-if="canPreview(file)"
                        type="text"
                        size="mini"
                        @click="handlePreview(file)"
                      >
                        预览
                      </el-button>
                    </div>
                  </el-card>
                </template>
              </div>
            </div>
            <div v-else class="no-attachment">
              <el-empty description="暂无会议附件" :image-size="50"></el-empty>
            </div>
          </div>
        </el-descriptions-item>
        <el-descriptions-item label="附件" :span="2" v-if="currentRecord.attachments && currentRecord.attachments.length">
          <div v-for="file in currentRecord.attachments" :key="file.id" class="attachment-item">
            <el-link type="primary" :href="file.url" target="_blank">
              <i class="el-icon-document"></i> {{ file.name }}
            </el-link>
          </div>
        </el-descriptions-item>
        <el-descriptions-item label="创建人">{{
          currentRecord.createBy
        }}</el-descriptions-item>
        <el-descriptions-item label="创建时间">{{
          formatDateTime(currentRecord.createTime)
        }}</el-descriptions-item>
        <el-descriptions-item label="更新人">{{
          currentRecord.updateBy
        }}</el-descriptions-item>
        <el-descriptions-item label="更新时间">{{
          formatDateTime(currentRecord.updateTime)
        }}</el-descriptions-item>
        <el-descriptions-item label="备注" :span="2">{{
          currentRecord.remark || "无"
        }}</el-descriptions-item>
      </el-descriptions>
      <span slot="footer">
        <el-button @click="detailDialogVisible = false">关闭</el-button>
        <el-button type="primary" @click="handleEdit(currentRecord)">编辑</el-button>
        <el-button
          type="primary"
          @click="handleEdit(currentRecord.id)"
          :disabled="currentRecord.delFlag === 1"
        >
          编辑
        </el-button>
      </span>
    </el-dialog>
@@ -213,7 +598,7 @@
    <el-dialog
      :title="`${isEditing ? '编辑' : '新增'}会议`"
      :visible.sync="editDialogVisible"
      width="700px"
      width="800px"
      :before-close="handleEditClose"
    >
      <el-form
@@ -227,35 +612,31 @@
          <el-input v-model="editForm.title" placeholder="请输入会议主题" />
        </el-form-item>
        <el-form-item label="会议类型" prop="meetingType">
          <el-select v-model="editForm.meetingType" placeholder="请选择会议类型" style="width: 100%">
            <el-option label="科研会议" value="research" />
            <el-option label="日常会议" value="daily" />
            <el-option label="项目会议" value="project" />
            <el-option label="部门会议" value="department" />
            <el-option label="评审会议" value="review" />
          </el-select>
        </el-form-item>
        <el-form-item label="参会人员" prop="participants">
        <el-form-item label="会议类型" prop="typeId">
          <el-select
            v-model="editForm.participants"
            multiple
            filterable
            placeholder="请选择参会人员"
            v-model="editForm.typeId"
            placeholder="请选择会议类型"
            style="width: 100%"
          >
            <el-option
              v-for="user in userList"
              :key="user.id"
              :label="user.name"
              :value="user.name"
            />
            <el-option label="科研会议" :value="1" />
            <el-option label="日常会议" :value="2" />
            <el-option label="项目会议" :value="3" />
            <el-option label="部门会议" :value="4" />
            <el-option label="评审会议" :value="5" />
          </el-select>
        </el-form-item>
        <el-form-item label="会议地点" prop="location">
          <el-input v-model="editForm.location" placeholder="请输入会议地点" />
        <el-form-item label="会议地点" prop="locationName">
          <el-select
            v-model="editForm.locationName"
            placeholder="请选择会议地点"
            style="width: 100%"
          >
            <el-option label="第一会议室" value="第一会议室" />
            <el-option label="第二会议室" value="第二会议室" />
            <el-option label="第三会议室" value="第三会议室" />
            <el-option label="线上会议" value="线上会议" />
          </el-select>
        </el-form-item>
        <el-row :gutter="20">
@@ -283,6 +664,19 @@
          </el-col>
        </el-row>
        <el-form-item label="提前提醒" prop="reminderMinutes">
          <el-input-number
            v-model="editForm.reminderMinutes"
            :min="0"
            :max="1440"
            :step="5"
            controls-position="right"
            style="width: 100%"
          >
            <template #append>分钟</template>
          </el-input-number>
        </el-form-item>
        <el-form-item label="会议概要" prop="summary">
          <el-input
            v-model="editForm.summary"
@@ -299,50 +693,248 @@
            v-model="editForm.content"
            type="textarea"
            :rows="6"
            placeholder="请输入会议具体内容"
            placeholder="请输入会议具体内容(支持HTML格式)"
          />
        </el-form-item>
        <el-form-item label="附件上传">
          <el-upload
            action="#"
            :auto-upload="false"
            :on-change="handleFileChange"
            :file-list="editForm.attachments"
            :limit="5"
            multiple
        <!-- 修改会议附件部分 -->
        <el-form-item label="会议附件">
          <div class="attachment-section">
            <div class="attachment-header">
              <i class="el-icon-paperclip"></i>
              <span class="attachment-title">会议附件上传</span>
              <span class="attachment-tip"
                >支持上传文档、图片等文件 (最多{{ attachmentLimit }}个)</span
              >
            </div>
            <!-- 使用 UploadAttachment 组件 -->
            <UploadAttachment
              ref="uploadAttachment"
              :file-list="attachmentFileList"
              :limit="attachmentLimit"
              :accept="attachmentAccept"
              @change="handleAttachmentChange"
              @upload-success="handleAttachmentUploadSuccess"
              @upload-error="handleAttachmentUploadError"
              @remove="handleAttachmentRemove"
            />
          </div>
          <!-- 会议附件列表 -->
          <div
            class="attachment-list"
            v-if="editForm.attachment && editForm.attachment.length > 0"
          >
            <el-button size="small" type="primary">点击上传</el-button>
            <div slot="tip" class="el-upload__tip">支持上传文档、图片等文件,单个文件不超过10MB</div>
          </el-upload>
            <div class="list-title">
              已上传附件 ({{ editForm.attachment.length }})
            </div>
            <el-table
              :data="editForm.attachment"
              style="width: 100%"
              size="small"
              border
            >
              <el-table-column label="文件名" min-width="200">
                <template #default="scope">
                  <i
                    class="el-icon-document"
                    style="margin-right: 8px; color: #409EFF;"
                  ></i>
                  <span class="file-name">{{ scope.row.name }}</span>
                </template>
              </el-table-column>
              <el-table-column label="文件类型" width="100">
                <template #default="scope">
                  <el-tag size="small">{{
                    getFileType(scope.row.name)
                  }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="文件大小" width="100">
                <template #default="scope">
                  <span>{{ formatFileSize(scope.row.size) }}</span>
                </template>
              </el-table-column>
              <el-table-column label="操作" width="200">
                <template #default="scope">
                  <el-button
                    size="mini"
                    type="primary"
                    @click="handlePreviewAttachment(scope.row)"
                  >
                    预览
                  </el-button>
                  <el-button
                    size="mini"
                    type="danger"
                    @click="handleRemoveMeetingAttachment(scope.$index)"
                  >
                    删除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-form-item>
        <el-divider>会议纪要(可会后补充)</el-divider>
        <el-form-item label="纪要内容" prop="recordcontent">
          <el-input
            v-model="editForm.recordcontent"
            type="textarea"
            :rows="6"
            placeholder="请输入会议纪要内容,包括会议决议、行动计划、责任人等信息"
            maxlength="1000"
            show-word-limit
          />
          <div style="color: #909399; font-size: 12px; margin-top: 4px;">
            提示:可记录会议讨论要点、决议事项、行动计划等
          </div>
        </el-form-item>
        <!-- 修改会议纪要附件部分 -->
        <el-form-item label="纪要附件">
          <div class="attachment-section">
            <div class="attachment-header">
              <i class="el-icon-paperclip"></i>
              <span class="attachment-title">纪要附件上传</span>
              <span class="attachment-tip"
                >支持上传与纪要相关的补充材料 (最多{{
                  minutesAttachmentLimit
                }}个)</span
              >
            </div>
            <UploadAttachment
              ref="uploadMinutesAttachment"
              :file-list="minutesAttachmentFileList"
              :limit="minutesAttachmentLimit"
              :accept="minutesAttachmentAccept"
              @change="handleMinutesAttachmentChange"
              @upload-success="handleMinutesUploadSuccess"
              @upload-error="handleMinutesUploadError"
              @remove="handleMinutesAttachmentRemove"
            />
          </div>
          <div
            class="attachment-list"
            v-if="
              editForm.recordattachment && editForm.recordattachment.length > 0
            "
          >
            <div class="list-title">
              已上传纪要附件 ({{ editForm.recordattachment.length }})
            </div>
            <el-table
              :data="editForm.recordattachment"
              style="width: 100%"
              size="small"
              border
            >
              <el-table-column label="文件名" min-width="200">
                <template #default="scope">
                  <i
                    class="el-icon-document"
                    style="margin-right: 8px; color: #409EFF;"
                  ></i>
                  <span class="file-name">{{ scope.row.name }}</span>
                </template>
              </el-table-column>
              <el-table-column label="文件类型" width="100">
                <template #default="scope">
                  <el-tag size="small">{{
                    getFileType(scope.row.name)
                  }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="文件大小" width="100">
                <template #default="scope">
                  <span>{{ formatFileSize(scope.row.size) }}</span>
                </template>
              </el-table-column>
              <el-table-column label="操作" width="200">
                <template #default="scope">
                  <el-button
                    size="mini"
                    type="primary"
                    @click="handlePreviewMinutesAttachment(scope.row)"
                  >
                    预览
                  </el-button>
                  <el-button
                    size="mini"
                    type="danger"
                    @click="handleRemoveMinutesAttachment(scope.$index)"
                  >
                    删除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input
            v-model="editForm.remark"
            type="textarea"
            :rows="2"
            placeholder="请输入备注信息"
          />
        </el-form-item>
      </el-form>
      <span slot="footer">
        <el-button @click="handleEditClose">取消</el-button>
        <el-button type="primary" @click="handleSave" :loading="saveLoading">
          {{ isEditing ? '保存' : '新增' }}
          {{ isEditing ? "保存" : "新增" }}
        </el-button>
      </span>
    </el-dialog>
    <!-- 文件预览弹窗 -->
    <FilePreviewDialog
      :visible="previewVisible"
      :file="currentPreviewFile"
      @close="previewVisible = false"
      @download="handleDownload"
    />
  </div>
</template>
<script>
import {
  meetinglist,
  meetingedit,
  meetingadd,
  meetingInfo,
  meetingDel,
  exporremeeting
} from "@/api/officeManagementApi";
import UploadAttachment from "@/components/UploadAttachment";
import FilePreviewDialog from "@/components/FilePreviewDialog";
import dayjs from "dayjs";
export default {
  name: 'MeetingManagement',
  name: "MeetingManagement",
  components: {
    UploadAttachment,
    FilePreviewDialog
  },
  data() {
    return {
      // 查询参数
      queryParams: {
        meetingType: '',
        location: '',
        title: "",
        meetingType: "",
        location: "",
        dateRange: [],
        status: ''
        status: ""
      },
      // 分页参数
      pagination: {
        currentPage: 1,
        pageNum: 1,
        pageSize: 10,
        total: 0
      },
@@ -352,336 +944,991 @@
      // 对话框显示状态
      detailDialogVisible: false,
      editDialogVisible: false,
      minutesDialogVisible: false,
      // 当前操作记录
      currentRecord: {},
      // 编辑状态
      isEditing: false,
      // 表格数据
      tableData: [],
      // 用户列表(用于选择参会人员)
      userList: [
        { id: 1, name: '张三' },
        { id: 2, name: '李四' },
        { id: 3, name: '王五' },
        { id: 4, name: '赵六' },
        { id: 5, name: '钱七' },
        { id: 6, name: '孙八' },
        { id: 7, name: '周九' },
        { id: 8, name: '吴十' }
      ],
      imageSize: "100px", // 图片显示大小
      // 编辑表单数据
      editForm: {
        title: '',
        meetingType: '',
        participants: [],
        location: '',
        startTime: '',
        endTime: '',
        summary: '',
        content: '',
        attachments: []
      },
      editForm: this.getDefaultFormData(),
      // 表单验证规则
      editRules: {
        title: [{ required: true, message: '请输入会议主题', trigger: 'blur' }],
        meetingType: [{ required: true, message: '请选择会议类型', trigger: 'change' }],
        participants: [{ required: true, message: '请选择参会人员', trigger: 'change' }],
        location: [{ required: true, message: '请输入会议地点', trigger: 'blur' }],
        startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
        endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
        summary: [{ required: true, message: '请输入会议概要', trigger: 'blur' }],
        content: [{ required: true, message: '请输入会议内容', trigger: 'blur' }]
      }
    }
        title: [{ required: true, message: "请输入会议主题", trigger: "blur" }],
        typeId: [
          { required: true, message: "请选择会议类型", trigger: "change" }
        ],
        locationId: [
          { required: true, message: "请选择会议地点", trigger: "change" }
        ],
        startTime: [
          { required: true, message: "请选择开始时间", trigger: "change" }
        ],
        endTime: [
          { required: true, message: "请选择结束时间", trigger: "change" }
        ],
        summary: [
          { required: true, message: "请输入会议概要", trigger: "blur" }
        ],
        content: [
          { required: true, message: "请输入会议内容", trigger: "blur" }
        ],
        reminderMinutes: [
          { required: true, message: "请输入提前提醒时间", trigger: "blur" }
        ]
      },
      // 附件相关配置
      attachmentLimit: 10,
      minutesAttachmentLimit: 5,
      attachmentAccept:
        ".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.zip,.rar",
      minutesAttachmentAccept: ".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx",
      // 文件预览相关
      previewVisible: false,
      currentPreviewFile: null,
      // 附件文件列表
      attachmentFileList: [],
      minutesAttachmentFileList: [],
      // 编辑表单数据 - 修改默认值
      editForm: this.getDefaultFormData()
    };
  },
  mounted() {
    this.loadData()
    this.loadData();
  },
  methods: {
    // 加载数据
    async loadData() {
      this.loading = true
      this.loading = true;
      try {
        // 模拟API调用
        await new Promise(resolve => setTimeout(resolve, 500))
        const params = {
          pageNum: this.pagination.pageNum,
          pageSize: this.pagination.pageSize,
          ...this.queryParams
        };
        // 生成模拟数据
        this.tableData = this.generateMockData()
        this.pagination.total = this.tableData.length
        // 处理时间范围查询
        if (
          this.queryParams.dateRange &&
          this.queryParams.dateRange.length === 2
        ) {
          params.startTime = this.queryParams.dateRange[0];
          params.endTime = this.queryParams.dateRange[1];
        }
        const response = await meetinglist(params);
        if (response.code === 200) {
          this.tableData = response.rows || [];
          this.pagination.total = response.total || 0;
        } else {
          this.$message.error(response.msg || "获取数据失败");
        }
      } catch (error) {
        console.error('加载数据失败:', error)
        this.$message.error('数据加载失败')
        console.error("加载数据失败:", error);
        this.$message.error("数据加载失败");
      } finally {
        this.loading = false
        this.loading = false;
      }
    },
    // 修改获取文件类型方法
    getFileType(fileName) {
      if (!fileName) return "other";
      const extension = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
      const pdfTypes = ["pdf"];
      const officeTypes = ["doc", "docx", "xls", "xlsx", "ppt", "pptx"];
      const zipTypes = ["zip", "rar"];
    // 生成模拟数据
    generateMockData() {
      const meetingTypes = ['research', 'daily', 'project', 'department', 'review']
      const statuses = ['pending', 'ongoing', 'completed', 'cancelled']
      const participantsPool = ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十']
      if (imageTypes.includes(extension)) return "image";
      if (pdfTypes.includes(extension)) return "pdf";
      if (officeTypes.includes(extension)) return "office";
      if (zipTypes.includes(extension)) return "archive";
      return "other";
    },
    // 判断是否为图片文件
    isImageFile(file) {
      if (!file || !file.name) return false;
      const imageExtensions = [
        "jpg",
        "jpeg",
        "png",
        "gif",
        "bmp",
        "webp",
        "svg"
      ];
      const extension = file.name
        .split(".")
        .pop()
        .toLowerCase();
      return imageExtensions.includes(extension);
    },
      return Array.from({ length: 10 }, (_, index) => {
        const participantCount = Math.floor(Math.random() * 5) + 2
        const participants = []
        for (let i = 0; i < participantCount; i++) {
          const randomIndex = Math.floor(Math.random() * participantsPool.length)
          participants.push(participantsPool[randomIndex])
    // 获取文件URL
    getFileUrl(file) {
      return file.url || file.path || file.fileUrl || "";
    },
    // 获取图片预览列表
    getImagePreviewList(file) {
      const url = this.getFileUrl(file);
      if (!url) return [];
      return [url];
    },
    // 获取文件类型文本
    getFileTypeText(file) {
      if (!file.name) return "文件";
      const ext = file.name
        .split(".")
        .pop()
        .toLowerCase();
      const typeMap = {
        pdf: "PDF文档",
        doc: "Word文档",
        docx: "Word文档",
        xls: "Excel表格",
        xlsx: "Excel表格",
        ppt: "PPT演示",
        pptx: "PPT演示",
        zip: "压缩包",
        rar: "压缩包",
        txt: "文本文档",
        jpg: "图片",
        jpeg: "图片",
        png: "图片",
        gif: "图片"
      };
      return typeMap[ext] || "文件";
    },
    // 判断文件是否可以预览
    canPreview(file) {
      if (!file.name) return false;
      const previewableExtensions = ["pdf", "jpg", "jpeg", "png", "gif", "txt"];
      const extension = file.name
        .split(".")
        .pop()
        .toLowerCase();
      return previewableExtensions.includes(extension);
    },
    // 文件预览
    handlePreview(file) {
      this.currentPreviewFile = {
        fileName: file.name,
        fileUrl: this.getFileUrl(file),
        fileType: this.getFileType(file.name)
      };
      this.previewVisible = true;
    },
    // 附件转换为上传文件列表
    parseAttachmentToFileList(attachments) {
      if (!attachments || !Array.isArray(attachments)) return [];
      return attachments.map((item, index) => ({
        uid: item.id || `attachment-${index}-${Date.now()}`,
        name: item.name || item.fileName,
        url: item.url || item.path || item.fileUrl,
        size: item.size,
        status: "success"
      }));
    },
    // 获取文件图标
    getFileIcon(fileName) {
      if (!fileName) return "el-icon-document";
      const ext = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const iconMap = {
        pdf: "el-icon-document",
        doc: "el-icon-document",
        docx: "el-icon-document",
        xls: "el-icon-document",
        xlsx: "el-icon-document",
        ppt: "el-icon-document",
        pptx: "el-icon-document",
        jpg: "el-icon-picture",
        jpeg: "el-icon-picture",
        png: "el-icon-picture",
        gif: "el-icon-picture",
        zip: "el-icon-folder",
        rar: "el-icon-folder"
      };
      return iconMap[ext] || "el-icon-document";
    },
    // 查看会议纪要
    handleViewMinutes(record) {
      this.currentRecord = { ...record };
      this.minutesDialogVisible = true;
    },
    // 解析附件JSON
    parseAttachments(attachmentJson) {
      if (!attachmentJson) return [];
      try {
        if (typeof attachmentJson === "string") {
          return JSON.parse(attachmentJson);
        }
        // 去重
        const uniqueParticipants = [...new Set(participants)]
        const startTime = new Date()
        startTime.setDate(startTime.getDate() + Math.floor(Math.random() * 30) - 15)
        startTime.setHours(9 + Math.floor(Math.random() * 8), Math.floor(Math.random() * 4) * 15, 0)
        const endTime = new Date(startTime)
        endTime.setHours(startTime.getHours() + Math.floor(Math.random() * 3) + 1)
        return {
          id: index + 1,
          title: `关于${['科研项目', '日常工作', '技术评审', '部门协调'][Math.floor(Math.random() * 4)]}的会议`,
          meetingType: meetingTypes[Math.floor(Math.random() * meetingTypes.length)],
          participants: uniqueParticipants,
          location: ['第一会议室', '第二会议室', '第三会议室', '线上会议'][Math.floor(Math.random() * 4)],
          startTime: startTime.toISOString(),
          endTime: endTime.toISOString(),
          summary: `本次会议主要讨论${['项目进展', '技术难题', '工作计划', '问题协调'][Math.floor(Math.random() * 4)]}等相关事宜`,
          content: `会议详细内容:\n1. 议题一讨论\n2. 议题二分析\n3. 下一步工作计划\n4. 任务分配`,
          status: statuses[Math.floor(Math.random() * statuses.length)],
          creator: '系统管理员',
          attachments: []
        }
      })
        return attachmentJson;
      } catch (error) {
        console.error("解析附件失败:", error);
        return [];
      }
    },
    // 获取会议类型标签样式
    getMeetingTypeTag(type) {
    getMeetingTypeTag(typeId) {
      const typeMap = {
        research: 'primary',
        daily: 'success',
        project: 'warning',
        department: 'info',
        review: 'danger'
      }
      return typeMap[type] || 'info'
        1: "primary", // 科研会议
        2: "success", // 日常会议
        3: "warning", // 项目会议
        4: "info", // 部门会议
        5: "danger" // 评审会议
      };
      return typeMap[typeId] || "info";
    },
    // 获取会议类型文本
    getMeetingTypeText(type) {
    getMeetingTypeText(typeId) {
      const textMap = {
        research: '科研会议',
        daily: '日常会议',
        project: '项目会议',
        department: '部门会议',
        review: '评审会议'
      }
      return textMap[type] || type
        1: "科研会议",
        2: "日常会议",
        3: "项目会议",
        4: "部门会议",
        5: "评审会议"
      };
      return textMap[typeId] || "其他";
    },
    // 获取状态标签样式
    getStatusTag(status) {
    // 获取会议状态标签样式
    getMeetingStatusTag(status) {
      const statusMap = {
        pending: 'primary',
        ongoing: 'success',
        completed: 'info',
        cancelled: 'danger'
      }
      return statusMap[status] || 'info'
        1: "primary", // 待开始
        2: "success", // 进行中
        3: "info", // 已结束
        4: "danger" // 已取消
      };
      return statusMap[status] || "info";
    },
    // 获取状态文本
    getStatusText(status) {
    // 获取会议状态文本
    getMeetingStatusText(status) {
      const textMap = {
        pending: '待开始',
        ongoing: '进行中',
        completed: '已结束',
        cancelled: '已取消'
      }
      return textMap[status] || status
        1: "待开始",
        2: "进行中",
        3: "已结束",
        4: "已取消"
      };
      return textMap[status] || "未知";
    },
    // 获取审核状态标签样式
    getApprovalStatusTag(status) {
      const statusMap = {
        0: "warning", // 待审核
        1: "success", // 已通过
        2: "danger" // 已驳回
      };
      return statusMap[status] || "info";
    },
    // 获取审核状态文本
    getApprovalStatusText(status) {
      const textMap = {
        0: "待审核",
        1: "已通过",
        2: "已驳回"
      };
      return textMap[status] || "未审核";
    },
    // 格式化日期时间
    formatDateTime(dateTime) {
      if (!dateTime) return ''
      const date = new Date(dateTime)
      return date.toLocaleString('zh-CN')
      if (!dateTime) return "";
      return dateTime.replace("T", " ");
    },
    // 计算会议持续时间
    calculateDuration(record) {
      if (!record.startTime || !record.endTime) return ''
      const start = new Date(record.startTime)
      const end = new Date(record.endTime)
      const duration = (end - start) / (1000 * 60) // 分钟数
      if (!record.startTime || !record.endTime) return "";
      const start = new Date(record.startTime);
      const end = new Date(record.endTime);
      const duration = (end - start) / (1000 * 60); // 分钟数
      if (duration < 60) {
        return `${Math.round(duration)}分钟`
        return `${Math.round(duration)}分钟`;
      } else {
        const hours = Math.floor(duration / 60)
        const minutes = Math.round(duration % 60)
        return minutes > 0 ? `${hours}小时${minutes}分钟` : `${hours}小时`
        const hours = Math.floor(duration / 60);
        const minutes = Math.round(duration % 60);
        return minutes > 0 ? `${hours}小时${minutes}分钟` : `${hours}小时`;
      }
    },
    // 文件大小格式化
    formatFileSize(bytes) {
      if (!bytes || bytes === 0) return "0 B";
      const k = 1024;
      const sizes = ["B", "KB", "MB", "GB"];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
    },
    // 查询处理
    handleQuery() {
      this.pagination.currentPage = 1
      this.loadData()
      this.pagination.pageNum = 1;
      this.loadData();
    },
    // 重置查询
    handleReset() {
      this.queryParams = {
        meetingType: '',
        location: '',
        title: "",
        meetingType: "",
        location: "",
        dateRange: [],
        status: ''
      }
      this.pagination.currentPage = 1
      this.loadData()
        status: ""
      };
      this.pagination.pageNum = 1;
      this.loadData();
    },
    // 查看详情
    handleView(record) {
      this.currentRecord = { ...record }
      this.detailDialogVisible = true
    async handleView(id) {
      try {
        const response = await meetingInfo(id);
        if (response.code === 200) {
          const data = response.data || {};
          // 解析附件
          if (data.attachment && typeof data.attachment === "string") {
            data.attachment = this.parseAttachments(data.attachment);
          }
          if (
            data.recordattachment &&
            typeof data.recordattachment === "string"
          ) {
            data.recordattachment = this.parseAttachments(
              data.recordattachment
            );
          }
          this.currentRecord = data;
          this.detailDialogVisible = true;
        } else {
          this.$message.error(response.msg || "获取详情失败");
        }
      } catch (error) {
        console.error("获取详情失败:", error);
        this.$message.error("获取详情失败");
      }
    },
    // 新增记录
    handleAdd() {
      this.isEditing = false
      this.editForm = this.getDefaultFormData()
      this.editDialogVisible = true
      this.isEditing = false;
      this.editForm = this.getDefaultFormData();
      this.attachmentFileList = [];
      this.minutesAttachmentFileList = [];
      this.editDialogVisible = true;
      this.$nextTick(() => {
        this.$refs.editForm && this.$refs.editForm.clearValidate()
      })
        this.$refs.editForm && this.$refs.editForm.clearValidate();
      });
    },
    // 编辑记录
    handleEdit(record) {
      this.isEditing = true
      this.currentRecord = record
      this.editForm = { ...record }
      this.editDialogVisible = true
      this.detailDialogVisible = false
      this.$nextTick(() => {
        this.$refs.editForm && this.$refs.editForm.clearValidate()
      })
    },
    // 复制记录
    handleCopy(record) {
      this.isEditing = false
      const copiedRecord = { ...record }
      delete copiedRecord.id
      copiedRecord.title = copiedRecord.title + '(复制)'
      this.editForm = copiedRecord
      this.editDialogVisible = true
      this.$nextTick(() => {
        this.$refs.editForm && this.$refs.editForm.clearValidate()
      })
    },
    // 删除记录
    handleDelete(record) {
      this.$confirm('确定要删除这条会议记录吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 模拟删除操作
        this.tableData = this.tableData.filter(item => item.id !== record.id)
        this.pagination.total = this.tableData.length
        this.$message.success('删除成功')
      }).catch(() => {})
    },
    // 保存记录
    async handleSave() {
    async handleEdit(id) {
      try {
        const valid = await this.$refs.editForm.validate()
        if (!valid) return
        const response = await meetingInfo(id);
        if (response.code === 200) {
          this.isEditing = true;
          const data = response.data || {};
        this.saveLoading = true
        // 模拟API调用
        await new Promise(resolve => setTimeout(resolve, 1000))
          // 解析附件
          if (data.attachment) {
            data.attachment = this.parseAttachments(data.attachment);
            this.attachmentFileList = this.parseAttachmentToFileList(
              data.attachment
            );
          }
          if (data.recordattachment) {
            data.recordattachment = this.parseAttachments(
              data.recordattachment
            );
            this.minutesAttachmentFileList = this.parseAttachmentToFileList(
              data.recordattachment
            );
          }
        this.$message.success(this.isEditing ? '保存成功' : '新增成功')
        this.editDialogVisible = false
        this.loadData()
          this.currentRecord = data;
          this.editForm = { ...data };
          this.editDialogVisible = true;
          this.detailDialogVisible = false;
          this.$nextTick(() => {
            this.$refs.editForm && this.$refs.editForm.clearValidate();
          });
        } else {
          this.$message.error(response.msg || "获取记录失败");
        }
      } catch (error) {
        console.error('保存失败:', error)
        this.$message.error('操作失败')
      } finally {
        this.saveLoading = false
        console.error("获取记录失败:", error);
        this.$message.error("获取记录失败");
      }
    },
    // 文件上传处理
    handleFileChange(file, fileList) {
      this.editForm.attachments = fileList
    // 复制记录
    async handleCopy(id) {
      try {
        const response = await meetingInfo(id);
        if (response.code === 200) {
          this.isEditing = false;
          const copiedRecord = { ...response.data };
          // 移除ID,生成新记录
          delete copiedRecord.id;
          delete copiedRecord.meetingNumber;
          copiedRecord.title = copiedRecord.title + "(复制)";
          copiedRecord.parentMeetingId = id;
          // 解析附件
          if (copiedRecord.attachment) {
            copiedRecord.attachmentFiles = this.parseAttachments(
              copiedRecord.attachment
            );
          }
          if (copiedRecord.recordattachment) {
            copiedRecord.recordattachmentFiles = this.parseAttachments(
              copiedRecord.recordattachment
            );
          }
          this.editForm = copiedRecord;
          this.editDialogVisible = true;
          this.$nextTick(() => {
            this.$refs.editForm && this.$refs.editForm.clearValidate();
          });
        } else {
          this.$message.error(response.msg || "获取记录失败");
        }
      } catch (error) {
        console.error("获取记录失败:", error);
        this.$message.error("获取记录失败");
      }
    },
    // 删除记录
    async handleDelete(record) {
      try {
        await this.$confirm("确定要删除这条会议记录吗?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        });
        const response = await meetingDel(record.id);
        if (response.code === 200) {
          this.$message.success("删除成功");
          this.loadData();
        } else {
          this.$message.error(response.msg || "删除失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          console.error("删除失败:", error);
          this.$message.error("删除失败");
        }
      }
    },
    // 附件变化处理
    handleAttachmentChange(fileList) {
      this.attachmentFileList = fileList;
    },
    handleMinutesAttachmentChange(fileList) {
      this.minutesAttachmentFileList = fileList;
    },
    // 附件上传成功处理
    handleAttachmentUploadSuccess({ file, fileList, response }) {
      if (response && response.code === 200) {
        if (!this.editForm.attachment) {
          this.editForm.attachment = [];
        }
        console.log(file, "file");
        console.log(response, "response");
        const attachmentObj = {
          name: file.name,
          url: response.data || file.url,
          fileName: file.name,
          size: file.size || 0,
          type: this.getFileExtension(file.name),
          createTime: dayjs().format("YYYY-MM-DD HH:mm:ss")
        };
        console.log(this.editForm.attachment, "this.editForm.attachment");
        this.editForm.attachment.push(attachmentObj);
        console.log(this.editForm.attachment, "this.editForm.attachment");
        this.$message.success("会议附件上传成功");
      }
    },
    handleMinutesUploadSuccess({ file, fileList, response }) {
      if (response && response.code === 200) {
        if (!this.editForm.recordattachment) {
          this.editForm.recordattachment = [];
        }
        const attachmentObj = {
          name: file.name,
          url: response.data || file.url,
          fileName: file.name,
          size: file.size || 0,
          type: this.getFileExtension(file.name),
          createTime: dayjs().format("YYYY-MM-DD HH:mm:ss")
        };
        this.editForm.recordattachment.push(attachmentObj);
        this.$message.success("纪要附件上传成功");
      }
    },
    // 附件上传错误处理
    handleAttachmentUploadError({ file, fileList, error }) {
      console.error("会议附件上传失败:", error);
      this.$message.error("文件上传失败,请重试");
    },
    handleMinutesUploadError({ file, fileList, error }) {
      console.error("纪要附件上传失败:", error);
      this.$message.error("文件上传失败,请重试");
    },
    // 附件移除处理
    handleAttachmentRemove(file) {
      if (file.url && this.editForm.attachment) {
        const index = this.editForm.attachment.findIndex(
          item =>
            item.url === file.url ||
            item.path === file.url ||
            item.fileUrl === file.url
        );
        if (index > -1) {
          this.editForm.attachment.splice(index, 1);
        }
      }
    },
    handleMinutesAttachmentRemove(file) {
      if (file.url && this.editForm.recordattachment) {
        const index = this.editForm.recordattachment.findIndex(
          item =>
            item.url === file.url ||
            item.path === file.url ||
            item.fileUrl === file.url
        );
        if (index > -1) {
          this.editForm.recordattachment.splice(index, 1);
        }
      }
    },
    // 手动删除附件
    handleRemoveMeetingAttachment(index) {
      if (this.editForm.attachment && this.editForm.attachment[index]) {
        this.editForm.attachment.splice(index, 1);
        this.attachmentFileList.splice(index, 1);
        this.$message.success("会议附件删除成功");
      }
    },
    handleRemoveMinutesAttachment(index) {
      if (
        this.editForm.recordattachment &&
        this.editForm.recordattachment[index]
      ) {
        this.editForm.recordattachment.splice(index, 1);
        this.minutesAttachmentFileList.splice(index, 1);
        this.$message.success("纪要附件删除成功");
      }
    },
    // 文件预览
    handlePreviewAttachment(file) {
      this.currentPreviewFile = {
        fileName: file.name || file.fileName,
        fileUrl: file.url || file.path || file.fileUrl,
        fileType: this.getFileType(file.name)
      };
      this.previewVisible = true;
    },
    handlePreviewMinutesAttachment(file) {
      this.currentPreviewFile = {
        fileName: file.name || file.fileName,
        fileUrl: file.url || file.path || file.fileUrl,
        fileType: this.getFileType(file.name)
      };
      this.previewVisible = true;
    },
    // 文件下载
    handleDownload(file) {
      if (file.url) {
        const link = document.createElement("a");
        link.href = file.url;
        link.download = file.name || file.fileName || "download";
        link.style.display = "none";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        this.$message.success("开始下载文件");
      } else {
        this.$message.warning("文件地址不存在,无法下载");
      }
    },
    // 获取文件扩展名
    getFileExtension(filename) {
      if (!filename) return "";
      return filename
        .split(".")
        .pop()
        .toLowerCase();
    },
    // 保存记录
    async handleSave() {
      try {
        const valid = await this.$refs.editForm.validate();
        if (!valid) return;
        // 验证时间
        if (this.editForm.startTime && this.editForm.endTime) {
          if (
            new Date(this.editForm.endTime) <= new Date(this.editForm.startTime)
          ) {
            this.$message.error("结束时间必须晚于开始时间");
            return;
          }
        }
        // 检查是否有未上传完成的文件
        const pendingFiles = [
          ...this.attachmentFileList.filter(item => item.status !== "success"),
          ...this.minutesAttachmentFileList.filter(
            item => item.status !== "success"
          )
        ];
        if (pendingFiles.length > 0) {
          this.$message.warning(
            "还有文件未上传完成,请先上传所有文件或移除未上传的文件"
          );
          return;
        }
        this.saveLoading = true;
        // 准备数据
        const formData = { ...this.editForm };
        // 处理附件为JSON字符串
        if (formData.attachment && Array.isArray(formData.attachment)) {
          formData.attachment = JSON.stringify(
            formData.attachment.map(item => ({
              name: item.name || item.fileName,
              url: item.url || item.path || item.fileUrl,
              size: item.size || 0,
              type:
                item.type || this.getFileExtension(item.name || item.fileName)
            }))
          );
        }
        if (
          formData.recordattachment &&
          Array.isArray(formData.recordattachment)
        ) {
          formData.recordattachment = JSON.stringify(
            formData.recordattachment.map(item => ({
              name: item.name || item.fileName,
              url: item.url || item.path || item.fileUrl,
              size: item.size || 0,
              type:
                item.type || this.getFileExtension(item.name || item.fileName)
            }))
          );
        }
        // 清理临时字段
        delete formData.attachmentFiles;
        delete formData.recordattachmentFiles;
        let response;
        if (this.isEditing) {
          response = await meetingedit(formData);
        } else {
          response = await meetingadd(formData);
        }
        if (response.code === 200) {
          this.$message.success(this.isEditing ? "保存成功" : "新增成功");
          this.editDialogVisible = false;
          this.loadData();
        } else {
          this.$message.error(
            response.msg || (this.isEditing ? "保存失败" : "新增失败")
          );
        }
      } catch (error) {
        console.error("保存失败:", error);
        this.$message.error(this.isEditing ? "保存失败" : "新增失败");
      } finally {
        this.saveLoading = false;
      }
    },
    // 关闭详情对话框
    handleDetailClose() {
      this.detailDialogVisible = false
      this.currentRecord = {}
      this.detailDialogVisible = false;
      this.currentRecord = {};
    },
    // 关闭编辑对话框
    handleEditClose() {
      this.editDialogVisible = false
      this.currentRecord = {}
      this.editDialogVisible = false;
      this.currentRecord = {};
      this.editForm = this.getDefaultFormData();
      this.attachmentFileList = [];
      this.minutesAttachmentFileList = [];
      this.$nextTick(() => {
        this.$refs.editForm && this.$refs.editForm.clearValidate()
      })
        this.$refs.editForm && this.$refs.editForm.clearValidate();
      });
    },
    // 导出数据
    exportData() {
      this.$message.success('导出功能开发中')
      const queryParams = this.queryParams;
      this.$modal
        .confirm("是否确认导出所有会议纪要数据项?")
        .then(() => {
          return exporremeeting(queryParams);
        })
        .then(response => {
          this.$download.name(response.msg);
        })
        .catch(() => {});
    },
    // 分页大小变化
    handleSizeChange(size) {
      this.pagination.pageSize = size
      this.pagination.currentPage = 1
      this.loadData()
      this.pagination.pageSize = size;
      this.pagination.pageNum = 1;
      this.loadData();
    },
    // 当前页变化
    handleCurrentChange(page) {
      this.pagination.currentPage = page
      this.loadData()
      this.pagination.pageNum = page;
      this.loadData();
    },
    // 排序变化
    handleSortChange(sort) {
      console.log('排序变化:', sort)
      console.log("排序变化:", sort);
    },
    // 获取默认表单数据
    // 默认表单数据
    getDefaultFormData() {
      return {
        title: '',
        meetingType: '',
        participants: [],
        location: '',
        startTime: '',
        endTime: '',
        summary: '',
        content: '',
        attachments: []
      }
        id: null,
        title: "",
        typeId: null,
        locationId: null,
        startTime: "",
        endTime: "",
        summary: "",
        content: "",
        attachment: [], // 改为数组
        recordcontent: "",
        recordattachment: [], // 改为数组
        recorderBy: "",
        remark: "",
        status: 1,
        reminderMinutes: 30,
        approvalStatus: 0
      };
    }
  }
}
};
</script>
<style scoped>
.meeting-management {
  padding: 20px;
}
.attachment-section {
  margin-bottom: 16px;
}
.attachment-header {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
  padding: 8px 0;
  border-bottom: 1px solid #ebeef5;
}
.attachment-title {
  font-weight: bold;
  margin: 0 8px;
}
.attachment-tip {
  font-size: 12px;
  color: #909399;
}
.attachment-list {
  margin-top: 16px;
}
.list-title {
  font-weight: bold;
  margin-bottom: 12px;
  color: #303133;
}
/* 图片附件样式 */
.image-attachment {
  border-radius: 4px;
  border: 1px solid #ebeef5;
  cursor: pointer;
  transition: all 0.3s;
  margin-bottom: 8px;
}
.image-attachment:hover {
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  transform: translateY(-2px);
}
.image-info {
  text-align: center;
  padding: 4px 0;
}
.image-info .file-name {
  font-size: 12px;
  color: #606266;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100px;
}
.image-info .file-actions {
  display: flex;
  justify-content: center;
  gap: 8px;
}
.image-loading,
.image-error {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100%;
  color: #909399;
  font-size: 12px;
}
.image-loading i,
.image-error i {
  font-size: 20px;
  margin-bottom: 4px;
}
/* 文件卡片样式 */
.file-card {
  width: 100%;
  min-height: 60px;
  margin-bottom: 8px;
}
.file-content {
  display: flex;
  align-items: center;
}
.file-icon {
  font-size: 24px;
  margin-right: 12px;
  color: #409eff;
  flex-shrink: 0;
}
.file-info {
  flex: 1;
  min-width: 0;
}
.file-name {
  font-size: 13px;
  font-weight: 500;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-meta {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #909399;
}
.file-actions {
  margin-top: 8px;
  text-align: right;
}
/* 附件网格布局 */
.attachment-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 16px;
}
.attachment-card {
  display: flex;
  flex-direction: column;
}
/* 响应式调整 */
@media (max-width: 768px) {
  .attachment-grid {
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    gap: 12px;
  }
  .image-attachment {
    width: 80px !important;
    height: 80px !important;
  }
}
.file-name {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 其他样式保持不变 */
.meeting-management {
  padding: 20px;
}
@@ -712,6 +1959,22 @@
  margin-bottom: 8px;
}
.minutes-content {
  margin-bottom: 16px;
}
.minutes-meta {
  border-top: 1px solid #e4e7ed;
  padding-top: 12px;
}
/* 响应式调整 */
@media (max-width: 768px) {
  .minutes-content .el-card {
    margin: 0 -20px;
  }
}
/* 响应式设计 */
@media (max-width: 768px) {
  .page-header {
@@ -725,4 +1988,116 @@
    justify-content: space-between;
  }
}
/* 新增附件相关样式 */
.attachment-upload-section {
  border: 1px solid #ebeef5;
  border-radius: 4px;
  padding: 15px;
  background-color: #fafafa;
}
.section-title {
  font-weight: bold;
  margin-bottom: 8px;
  color: #303133;
}
.upload-tip {
  font-size: 12px;
  color: #909399;
  margin-bottom: 10px;
}
.uploaded-files {
  margin-top: 10px;
}
.file-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px;
  border-bottom: 1px solid #f0f0f0;
}
.file-item:last-child {
  border-bottom: none;
}
.file-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attachment-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 10px;
}
.file-card {
  transition: all 0.3s ease;
}
.file-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.file-content {
  display: flex;
  align-items: center;
}
.file-icon {
  font-size: 24px;
  color: #409eff;
  margin-right: 10px;
}
.file-info {
  flex: 1;
}
.file-name {
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-meta {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #909399;
}
.file-actions {
  margin-top: 8px;
  text-align: center;
}
.file-link {
  display: flex;
  align-items: center;
}
.no-attachment {
  text-align: center;
  padding: 20px;
  color: #909399;
}
/* 响应式设计 */
@media (max-width: 768px) {
  .attachment-grid {
    grid-template-columns: 1fr;
  }
}
</style>