| | |
| | | <div class="meeting-management"> |
| | | <!-- 页面头部 --> |
| | | <div class="page-header"> |
| | | <h2>会议管理</h2> |
| | | <div class="header-actions"> |
| | | <el-button type="primary" icon="el-icon-plus" @click="handleAdd"> |
| | | 新建会议 |
| | |
| | | style="width: 150px" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="时间范围"> |
| | | <el-form-item label="会议开始时间范围"> |
| | | <el-date-picker |
| | | v-model="queryParams.dateRange" |
| | | type="daterange" |
| | |
| | | clearable |
| | | placeholder="请选择" |
| | | > |
| | | <el-option label="待开始" value="pending" /> |
| | | <el-option label="进行中" value="ongoing" /> |
| | | <el-option label="已结束" value="completed" /> |
| | | <el-option label="已取消" value="cancelled" /> |
| | | <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> |
| | |
| | | style="width: 100%" |
| | | @sort-change="handleSortChange" |
| | | > |
| | | <!-- <el-table-column prop="id" label="ID" width="80" fixed /> --> |
| | | <el-table-column prop="id" label="ID" width="80" fixed align="center" /> |
| | | <el-table-column |
| | | prop="title" |
| | | align="center" |
| | |
| | | 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> |
| | |
| | | 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 |
| | | align="center" |
| | | prop="status" |
| | | label="状态" |
| | | label="会议状态" |
| | | width="100" |
| | | fixed="right" |
| | | > |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusTag(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | <el-tag :type="getMeetingStatusTag(scope.row.status)"> |
| | | {{ getMeetingStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="participants" |
| | | label="参会人员" |
| | | align="center" |
| | | width="180" |
| | | show-overflow-tooltip |
| | | > |
| | | <template #default="scope"> |
| | | <span>{{ scope.row.participants.join(", ") }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | prop="organizerName" |
| | | label="组织者" |
| | | width="120" |
| | | /> |
| | | <el-table-column |
| | | align="center" |
| | | prop="location" |
| | | prop="locationName" |
| | | label="会议地点" |
| | | width="150" |
| | | /> |
| | |
| | | size="mini" |
| | | type="text" |
| | | @click="handleViewMinutes(scope.row)" |
| | | :disabled="!scope.row.meetingMinutes" |
| | | :disabled="!scope.row.recordcontent" |
| | | > |
| | | {{ scope.row.meetingMinutes ? "查看纪要" : "暂无" }} |
| | | {{ 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="操作" 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> |
| | |
| | | type="text" |
| | | @click="handleDelete(scope.row)" |
| | | style="color: #F56C6C;" |
| | | :disabled="scope.row.delFlag === 1" |
| | | > |
| | | 删除 |
| | | </el-button> |
| | |
| | | <!-- 分页 --> |
| | | <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.meetingMinutes"> |
| | | <div v-if="currentRecord.recordcontent"> |
| | | <el-alert |
| | | title="会议纪要详情" |
| | | type="info" |
| | |
| | | <div |
| | | style="white-space: pre-line; line-height: 1.6; max-height: 300px; overflow-y: auto;" |
| | | > |
| | | {{ currentRecord.meetingMinutes }} |
| | | {{ currentRecord.recordcontent }} |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | |
| | | <div |
| | | class="minutes-attachments" |
| | | v-if=" |
| | | currentRecord.minutesAttachments && |
| | | currentRecord.minutesAttachments.length > 0 |
| | | currentRecord.recordattachment && |
| | | parseAttachments(currentRecord.recordattachment).length > 0 |
| | | " |
| | | > |
| | | <el-divider content-position="left">会议纪要附件</el-divider> |
| | | <div class="attachment-list"> |
| | | <div |
| | | v-for="file in currentRecord.minutesAttachments" |
| | | :key="file.id" |
| | | v-for="file in parseAttachments(currentRecord.recordattachment)" |
| | | :key="file.id || file.name" |
| | | class="attachment-item" |
| | | > |
| | | <el-link |
| | |
| | | class="file-link" |
| | | > |
| | | <i |
| | | :class="getFileIcon(file.type)" |
| | | :class="getFileIcon(file.type || file.name)" |
| | | style="margin-right: 8px;" |
| | | ></i> |
| | | {{ file.name }} |
| | | <span class="file-size">({{ formatFileSize(file.size) }})</span> |
| | | <span class="file-size" v-if="file.size" |
| | | >({{ formatFileSize(file.size) }})</span |
| | | > |
| | | </el-link> |
| | | </div> |
| | | </div> |
| | |
| | | style="margin-top: 16px; color: #909399; font-size: 12px;" |
| | | > |
| | | <span |
| | | >创建时间: |
| | | {{ formatDateTime(currentRecord.minutesCreateTime) }}</span |
| | | >记录时间: {{ formatDateTime(currentRecord.recorderTime) }}</span |
| | | > |
| | | <span style="margin-left: 16px;" |
| | | >创建人: {{ currentRecord.minutesCreator || "系统" }}</span |
| | | >记录人: {{ currentRecord.recorderBy || "未指定" }}</span |
| | | > |
| | | </div> |
| | | </div> |
| | |
| | | <el-button @click="minutesDialogVisible = false">关闭</el-button> |
| | | <el-button |
| | | type="primary" |
| | | @click="handleEdit(currentRecord)" |
| | | v-if="currentRecord.meetingMinutes" |
| | | @click="handleEdit(currentRecord.id)" |
| | | v-if="currentRecord.recordcontent" |
| | | > |
| | | 编辑纪要 |
| | | </el-button> |
| | | </span> |
| | | </el-dialog> |
| | | |
| | | <!-- 查看详情对话框 --> |
| | | <el-dialog |
| | | :title="`会议详情 - ${currentRecord.title || ''}`" |
| | |
| | | width="800px" |
| | | :before-close="handleDetailClose" |
| | | > |
| | | <el-descriptions :column="2" border v-if="currentRecord"> |
| | | <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="组织者">{{ |
| | | 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="持续时间">{{ |
| | | calculateDuration(currentRecord) |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="创建人">{{ |
| | | currentRecord.creator |
| | | <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 |
| | | v-html="currentRecord.content" |
| | | style="white-space: pre-line; max-height: 300px; overflow-y: auto;" |
| | | > |
| | | {{ currentRecord.content }} |
| | | </div> |
| | | ></div> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议详情附件" :span="2"> |
| | | |
| | | <!-- 会议附件 --> |
| | | <el-descriptions-item label="会议附件" :span="2"> |
| | | <div class="detail-attachments"> |
| | | <div |
| | | v-if=" |
| | | currentRecord.detailAttachments && |
| | | currentRecord.detailAttachments.length > 0 |
| | | currentRecord.attachment && |
| | | parseAttachments(currentRecord.attachment).length > 0 |
| | | " |
| | | class="attachment-grid" |
| | | > |
| | | <div |
| | | v-for="file in currentRecord.detailAttachments" |
| | | :key="file.id" |
| | | v-for="file in parseAttachments(currentRecord.attachment)" |
| | | :key="file.id || file.name" |
| | | class="attachment-card" |
| | | > |
| | | <el-card shadow="hover" class="file-card"> |
| | | <div class="file-content"> |
| | | <i :class="getFileIcon(file.type)" class="file-icon"></i> |
| | | <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-size">{{ |
| | | <span class="file-size" v-if="file.size">{{ |
| | | formatFileSize(file.size) |
| | | }}</span> |
| | | <span class="file-time">{{ |
| | | formatDateTime(file.uploadTime) |
| | | }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="file-actions"> |
| | | <el-button |
| | | type="text" |
| | | size="mini" |
| | | @click="handlePreview(file)" |
| | | >预览</el-button |
| | | > |
| | | <div class="file-actions" v-if="file.url"> |
| | | <el-button |
| | | type="text" |
| | | size="mini" |
| | |
| | | </div> |
| | | </div> |
| | | <div v-else class="no-attachment"> |
| | | <el-empty |
| | | description="暂无会议详情附件" |
| | | :image-size="50" |
| | | ></el-empty> |
| | | <el-empty description="暂无会议附件" :image-size="50"></el-empty> |
| | | </div> |
| | | </div> |
| | | </el-descriptions-item> |
| | | |
| | | <!-- 会议概要附件 --> |
| | | <el-descriptions-item label="会议概要附件" :span="2"> |
| | | <div class="summary-attachments"> |
| | | <div |
| | | v-if=" |
| | | currentRecord.summaryAttachments && |
| | | currentRecord.summaryAttachments.length > 0 |
| | | " |
| | | class="attachment-list" |
| | | > |
| | | <div |
| | | v-for="file in currentRecord.summaryAttachments" |
| | | :key="file.id" |
| | | class="attachment-item" |
| | | > |
| | | <el-link |
| | | type="primary" |
| | | :href="file.url" |
| | | target="_blank" |
| | | class="file-link" |
| | | > |
| | | <i |
| | | :class="getFileIcon(file.type)" |
| | | style="margin-right: 8px;" |
| | | ></i> |
| | | {{ file.name }} |
| | | <span class="file-size" |
| | | >({{ formatFileSize(file.size) }})</span |
| | | > |
| | | </el-link> |
| | | </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="创建人">{{ |
| | | 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> |
| | | |
| | |
| | | <el-input v-model="editForm.title" placeholder="请输入会议主题" /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="会议类型" prop="meetingType"> |
| | | <el-form-item label="会议类型" prop="typeId"> |
| | | <el-select |
| | | v-model="editForm.meetingType" |
| | | v-model="editForm.typeId" |
| | | 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-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="participants"> |
| | | <el-form-item label="会议地点" prop="locationId"> |
| | | <el-select |
| | | v-model="editForm.participants" |
| | | multiple |
| | | filterable |
| | | placeholder="请选择参会人员" |
| | | v-model="editForm.locationId" |
| | | 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-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="会议地点" prop="location"> |
| | | <el-input v-model="editForm.location" placeholder="请输入会议地点" /> |
| | | </el-form-item> |
| | | <el-form-item label="会议详情附件"> |
| | | <div class="attachment-upload-section"> |
| | | <div class="section-title">会议相关资料</div> |
| | | <div class="upload-tip">上传会议讨论材料、背景资料等相关文件</div> |
| | | <el-upload |
| | | class="detail-upload" |
| | | action="#" |
| | | :auto-upload="false" |
| | | :on-change="handleDetailFileChange" |
| | | :file-list="editForm.detailAttachments" |
| | | :limit="10" |
| | | multiple |
| | | > |
| | | <el-button size="small" type="primary" icon="el-icon-upload"> |
| | | 上传详情附件 |
| | | </el-button> |
| | | <div slot="tip" class="el-upload__tip"> |
| | | 支持文档、图片等格式,单个文件不超过20MB |
| | | </div> |
| | | </el-upload> |
| | | <div |
| | | v-if=" |
| | | editForm.detailAttachments && |
| | | editForm.detailAttachments.length > 0 |
| | | " |
| | | class="uploaded-files" |
| | | > |
| | | <div |
| | | v-for="(file, index) in editForm.detailAttachments" |
| | | :key="file.uid || index" |
| | | class="file-item" |
| | | > |
| | | <span class="file-name">{{ file.name }}</span> |
| | | <el-button |
| | | type="text" |
| | | size="mini" |
| | | @click="handleRemoveDetailFile(index)" |
| | | style="color: #F56C6C;" |
| | | > |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="会议概要" prop="summary"> |
| | | <el-input |
| | | v-model="editForm.summary" |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请输入会议概要" |
| | | maxlength="200" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <!-- 会议概要附件上传 --> |
| | | <el-form-item label="会议概要附件"> |
| | | <div class="attachment-upload-section"> |
| | | <div class="section-title">会议纪要材料</div> |
| | | <div class="upload-tip">上传会议纪要、决议文件等相关材料</div> |
| | | <el-upload |
| | | class="summary-upload" |
| | | action="#" |
| | | :auto-upload="false" |
| | | :on-change="handleSummaryFileChange" |
| | | :file-list="editForm.summaryAttachments" |
| | | :limit="5" |
| | | multiple |
| | | > |
| | | <el-button size="small" type="primary" icon="el-icon-upload"> |
| | | 上传概要附件 |
| | | </el-button> |
| | | <div slot="tip" class="el-upload__tip"> |
| | | 支持PDF、Word等文档格式,单个文件不超过10MB |
| | | </div> |
| | | </el-upload> |
| | | <div |
| | | v-if=" |
| | | editForm.summaryAttachments && |
| | | editForm.summaryAttachments.length > 0 |
| | | " |
| | | class="uploaded-files" |
| | | > |
| | | <div |
| | | v-for="(file, index) in editForm.summaryAttachments" |
| | | :key="file.uid || index" |
| | | class="file-item" |
| | | > |
| | | <span class="file-name">{{ file.name }}</span> |
| | | <el-button |
| | | type="text" |
| | | size="mini" |
| | | @click="handleRemoveSummaryFile(index)" |
| | | style="color: #F56C6C;" |
| | | > |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="开始时间" prop="startTime"> |
| | |
| | | </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" |
| | |
| | | v-model="editForm.content" |
| | | type="textarea" |
| | | :rows="6" |
| | | placeholder="请输入会议具体内容" |
| | | placeholder="请输入会议具体内容(支持HTML格式)" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <!-- 会议纪要附件上传 --> |
| | | <el-form-item label="会议纪要附件"> |
| | | <el-form-item label="会议附件"> |
| | | <div class="attachment-upload-section"> |
| | | <div class="upload-tip">上传会议相关资料文件</div> |
| | | <el-upload |
| | | class="attachment-upload" |
| | | action="#" |
| | | :auto-upload="false" |
| | | :on-change="handleAttachmentChange" |
| | | :file-list="editForm.attachmentFiles" |
| | | :limit="10" |
| | | multiple |
| | | > |
| | | <el-button size="small" type="primary" icon="el-icon-upload"> |
| | | 上传会议附件 |
| | | </el-button> |
| | | <div slot="tip" class="el-upload__tip"> |
| | | 支持文档、图片等格式,单个文件不超过20MB |
| | | </div> |
| | | </el-upload> |
| | | <div |
| | | v-if=" |
| | | editForm.attachmentFiles && editForm.attachmentFiles.length > 0 |
| | | " |
| | | class="uploaded-files" |
| | | > |
| | | <div |
| | | v-for="(file, index) in editForm.attachmentFiles" |
| | | :key="file.uid || index" |
| | | class="file-item" |
| | | > |
| | | <span class="file-name">{{ file.name }}</span> |
| | | <el-button |
| | | type="text" |
| | | size="mini" |
| | | @click="handleRemoveAttachmentFile(index)" |
| | | style="color: #F56C6C;" |
| | | > |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </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-upload-section"> |
| | | <div class="upload-tip">上传与会议纪要相关的补充材料</div> |
| | | <el-upload |
| | | class="minutes-upload" |
| | | action="#" |
| | | :auto-upload="false" |
| | | :on-change="handleMinutesFileChange" |
| | | :file-list="editForm.minutesAttachments" |
| | | :on-change="handleMinutesAttachmentChange" |
| | | :file-list="editForm.recordattachmentFiles" |
| | | :limit="5" |
| | | multiple |
| | | > |
| | |
| | | </el-upload> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="会议纪要" prop="meetingMinutes"> |
| | | |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input |
| | | v-model="editForm.meetingMinutes" |
| | | v-model="editForm.remark" |
| | | type="textarea" |
| | | :rows="6" |
| | | placeholder="请输入会议纪要内容,包括会议决议、行动计划、责任人等信息" |
| | | maxlength="1000" |
| | | show-word-limit |
| | | :rows="2" |
| | | placeholder="请输入备注信息" |
| | | /> |
| | | <div style="color: #909399; font-size: 12px; margin-top: 4px;"> |
| | | 提示:可记录会议讨论要点、决议事项、行动计划等 |
| | | </div> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { |
| | | meetinglist, |
| | | meetingedit, |
| | | meetingadd, |
| | | meetingInfo, |
| | | meetingDel, |
| | | exporremeeting |
| | | } from "@/api/officeManagementApi"; |
| | | |
| | | export default { |
| | | name: "MeetingManagement", |
| | | data() { |
| | | return { |
| | | // 查询参数 |
| | | queryParams: { |
| | | title: "", |
| | | meetingType: "", |
| | | location: "", |
| | | dateRange: [], |
| | |
| | | }, |
| | | // 分页参数 |
| | | pagination: { |
| | | currentPage: 1, |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | total: 0 |
| | | }, |
| | |
| | | // 对话框显示状态 |
| | | 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: "吴十" } |
| | | ], |
| | | minutesDialogVisible: false, // 会议纪要对话框显示状态 |
| | | |
| | | // 编辑表单数据 |
| | | editForm: { |
| | | title: "", |
| | | meetingType: "", |
| | | participants: [], |
| | | location: "", |
| | | startTime: "", |
| | | endTime: "", |
| | | summary: "", |
| | | content: "", |
| | | attachments: [], |
| | | detailAttachments: [], // 会议详情附件 |
| | | summaryAttachments: [], // 会议概要附件 |
| | | minutesAttachments: [] // 会议纪要附件 |
| | | }, |
| | | editForm: this.getDefaultFormData(), |
| | | // 表单验证规则 |
| | | editRules: { |
| | | title: [{ required: true, message: "请输入会议主题", trigger: "blur" }], |
| | | meetingType: [ |
| | | typeId: [ |
| | | { required: true, message: "请选择会议类型", trigger: "change" } |
| | | ], |
| | | participants: [ |
| | | { required: true, message: "请选择参会人员", trigger: "change" } |
| | | ], |
| | | location: [ |
| | | { required: true, message: "请输入会议地点", trigger: "blur" } |
| | | locationId: [ |
| | | { required: true, message: "请选择会议地点", trigger: "change" } |
| | | ], |
| | | startTime: [ |
| | | { required: true, message: "请选择开始时间", trigger: "change" } |
| | |
| | | content: [ |
| | | { required: true, message: "请输入会议内容", trigger: "blur" } |
| | | ], |
| | | meetingMinutes: [ |
| | | { |
| | | max: 1000, |
| | | message: "会议纪要长度不能超过1000个字符", |
| | | trigger: "blur" |
| | | } |
| | | reminderMinutes: [ |
| | | { required: true, message: "请输入提前提醒时间", trigger: "blur" } |
| | | ] |
| | | } |
| | | }; |
| | |
| | | async loadData() { |
| | | 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("数据加载失败"); |
| | |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | getFileIcon(fileType) { |
| | | |
| | | // 获取文件图标 |
| | | getFileIcon(fileName) { |
| | | if (!fileName) return "el-icon-document"; |
| | | const ext = fileName |
| | | .split(".") |
| | | .pop() |
| | | .toLowerCase(); |
| | | const iconMap = { |
| | | pdf: "el-icon-document", |
| | | doc: "el-icon-document", |
| | |
| | | zip: "el-icon-folder", |
| | | rar: "el-icon-folder" |
| | | }; |
| | | return iconMap[fileType] || "el-icon-document"; |
| | | return iconMap[ext] || "el-icon-document"; |
| | | }, |
| | | |
| | | // 查看会议纪要 |
| | | handleViewMinutes(record) { |
| | | this.currentRecord = { ...record }; |
| | | this.minutesDialogVisible = true; |
| | | }, |
| | | |
| | | // 生成模拟数据 |
| | | generateMockData() { |
| | | const meetingTypes = [ |
| | | "research", |
| | | "daily", |
| | | "project", |
| | | "department", |
| | | "review" |
| | | ]; |
| | | const statuses = ["pending", "ongoing", "completed", "cancelled"]; |
| | | const participantsPool = [ |
| | | "张三", |
| | | "李四", |
| | | "王五", |
| | | "赵六", |
| | | "钱七", |
| | | "孙八", |
| | | "周九", |
| | | "吴十" |
| | | ]; |
| | | |
| | | // 会议纪要示例内容 |
| | | const minutesExamples = [ |
| | | `会议决议: |
| | | 1. 项目计划调整至下周一正式启动 |
| | | 2. 技术方案由张三负责完善 |
| | | 3. 下周进行技术评审会议 |
| | | |
| | | 行动计划: |
| | | - 李四:准备技术文档(截止周五) |
| | | - 王五:协调资源分配(截止周四) |
| | | - 全体:参加技术培训(周三下午)`, |
| | | |
| | | `讨论要点: |
| | | 1. 当前项目进度正常,需加强质量管控 |
| | | 2. 客户反馈问题需要优先处理 |
| | | 3. 下阶段工作重点明确 |
| | | |
| | | 任务分配: |
| | | ✅ 已完成:需求分析、技术方案 |
| | | 🔷 进行中:开发实施(负责人:赵六) |
| | | ⏳ 待开始:测试验收`, |
| | | |
| | | `重要决议: |
| | | • 通过预算调整方案 |
| | | • 确定新成员加入时间 |
| | | • 批准设备采购申请 |
| | | |
| | | 后续安排: |
| | | 📅 下次会议时间:2024-01-15 14:00 |
| | | 📍 会议地点:第一会议室 |
| | | 👥 必须参会人员:张三、李四` |
| | | ]; |
| | | |
| | | 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]); |
| | | // 解析附件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 |
| | | ); |
| | | |
| | | // 随机决定是否有会议纪要(60%概率有纪要) |
| | | const hasMinutes = Math.random() > 0.4; |
| | | const meetingMinutes = hasMinutes |
| | | ? minutesExamples[Math.floor(Math.random() * minutesExamples.length)] |
| | | : ""; |
| | | const hasDetailAttachments = Math.random() > 0.5; |
| | | const hasSummaryAttachments = Math.random() > 0.6; |
| | | const hasMinutesAttachments = Math.random() > 0.7; |
| | | return { |
| | | id: index + 1, |
| | | title: `关于${ |
| | | ["科研项目", "日常工作", "技术评审", "部门协调"][ |
| | | Math.floor(Math.random() * 4) |
| | | ] |
| | | }的会议`, |
| | | // 添加分类型附件 |
| | | detailAttachments: hasDetailAttachments ? [ |
| | | { |
| | | id: `detail-${index}-1`, |
| | | name: `会议材料-${index}.pdf`, |
| | | type: 'pdf', |
| | | size: 2048000, |
| | | url: '/api/files/meeting-material.pdf', |
| | | uploadTime: new Date(startTime.getTime() - 3600000).toISOString() |
| | | }, |
| | | { |
| | | id: `detail-${index}-2`, |
| | | name: `背景资料-${index}.docx`, |
| | | type: 'docx', |
| | | size: 1024000, |
| | | url: '/api/files/background.docx', |
| | | uploadTime: new Date(startTime.getTime() - 7200000).toISOString() |
| | | } |
| | | ] : [], |
| | | |
| | | summaryAttachments: hasSummaryAttachments ? [ |
| | | { |
| | | id: `summary-${index}-1`, |
| | | name: `会议纪要-${index}.pdf`, |
| | | type: 'pdf', |
| | | size: 512000, |
| | | url: '/api/files/meeting-summary.pdf', |
| | | uploadTime: new Date(startTime.getTime() + 3600000).toISOString() |
| | | } |
| | | ] : [], |
| | | |
| | | minutesAttachments: hasMinutesAttachments ? [ |
| | | { |
| | | id: `minutes-${index}-1`, |
| | | name: `补充材料-${index}.jpg`, |
| | | type: 'jpg', |
| | | size: 1024000, |
| | | url: '/api/files/supplement.jpg', |
| | | uploadTime: new Date(startTime.getTime() + 7200000).toISOString() |
| | | } |
| | | ] : [], |
| | | 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. 任务分配`, |
| | | meetingMinutes: meetingMinutes, |
| | | minutesCreateTime: hasMinutes |
| | | ? new Date(startTime.getTime() + 3600000).toISOString() |
| | | : "", |
| | | minutesCreator: hasMinutes ? participants[0] : "", |
| | | 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" |
| | | 1: "primary", // 科研会议 |
| | | 2: "success", // 日常会议 |
| | | 3: "warning", // 项目会议 |
| | | 4: "info", // 部门会议 |
| | | 5: "danger" // 评审会议 |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | return typeMap[typeId] || "info"; |
| | | }, |
| | | |
| | | // 获取会议类型文本 |
| | | getMeetingTypeText(type) { |
| | | getMeetingTypeText(typeId) { |
| | | const textMap = { |
| | | research: "科研会议", |
| | | daily: "日常会议", |
| | | project: "项目会议", |
| | | department: "部门会议", |
| | | review: "评审会议" |
| | | 1: "科研会议", |
| | | 2: "日常会议", |
| | | 3: "项目会议", |
| | | 4: "部门会议", |
| | | 5: "评审会议" |
| | | }; |
| | | return textMap[type] || type; |
| | | return textMap[typeId] || "其他"; |
| | | }, |
| | | |
| | | // 获取状态标签样式 |
| | | getStatusTag(status) { |
| | | // 获取会议状态标签样式 |
| | | getMeetingStatusTag(status) { |
| | | const statusMap = { |
| | | pending: "primary", |
| | | ongoing: "success", |
| | | completed: "info", |
| | | cancelled: "danger" |
| | | 1: "primary", // 待开始 |
| | | 2: "success", // 进行中 |
| | | 3: "info", // 已结束 |
| | | 4: "danger" // 已取消 |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }, |
| | | |
| | | // 获取状态文本 |
| | | getStatusText(status) { |
| | | // 获取会议状态文本 |
| | | getMeetingStatusText(status) { |
| | | const textMap = { |
| | | pending: "待开始", |
| | | ongoing: "进行中", |
| | | completed: "已结束", |
| | | cancelled: "已取消" |
| | | 1: "待开始", |
| | | 2: "进行中", |
| | | 3: "已结束", |
| | | 4: "已取消" |
| | | }; |
| | | return textMap[status] || status; |
| | | 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"); |
| | | return dateTime.replace("T", " "); |
| | | }, |
| | | |
| | | // 计算会议持续时间 |
| | |
| | | } |
| | | }, |
| | | |
| | | // 格式化文件大小 |
| | | formatFileSize(bytes) { |
| | | if (!bytes) 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.pagination.pageNum = 1; |
| | | this.loadData(); |
| | | }, |
| | | |
| | | // 重置查询 |
| | | handleReset() { |
| | | this.queryParams = { |
| | | title: "", |
| | | meetingType: "", |
| | | location: "", |
| | | dateRange: [], |
| | | status: "" |
| | | }; |
| | | this.pagination.currentPage = 1; |
| | | 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) { |
| | | this.currentRecord = response.data || {}; |
| | | this.detailDialogVisible = true; |
| | | } else { |
| | | this.$message.error(response.msg || "获取详情失败"); |
| | | } |
| | | } catch (error) { |
| | | console.error("获取详情失败:", error); |
| | | this.$message.error("获取详情失败"); |
| | | } |
| | | }, |
| | | |
| | | // 新增记录 |
| | |
| | | }, |
| | | |
| | | // 编辑记录 |
| | | 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(); |
| | | }); |
| | | async handleEdit(id) { |
| | | try { |
| | | const response = await meetingInfo(id); |
| | | if (response.code === 200) { |
| | | this.isEditing = true; |
| | | this.currentRecord = response.data || {}; |
| | | this.editForm = { ...response.data }; |
| | | |
| | | // 解析附件 |
| | | if (this.editForm.attachment) { |
| | | this.editForm.attachmentFiles = this.parseAttachments( |
| | | this.editForm.attachment |
| | | ); |
| | | } |
| | | if (this.editForm.recordattachment) { |
| | | this.editForm.recordattachmentFiles = this.parseAttachments( |
| | | this.editForm.recordattachment |
| | | ); |
| | | } |
| | | |
| | | 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("获取记录失败"); |
| | | } |
| | | }, |
| | | |
| | | // 复制记录 |
| | | 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(); |
| | | }); |
| | | 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("获取记录失败"); |
| | | } |
| | | }, |
| | | |
| | | // 删除记录 |
| | | 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; |
| | | async handleDelete(record) { |
| | | try { |
| | | await this.$confirm("确定要删除这条会议记录吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning" |
| | | }); |
| | | |
| | | const response = await meetingDel(record.id); |
| | | if (response.code === 200) { |
| | | this.$message.success("删除成功"); |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | // 新增方法:格式化文件大小 |
| | | formatFileSize(bytes) { |
| | | if (!bytes) 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]; |
| | | this.loadData(); |
| | | } else { |
| | | this.$message.error(response.msg || "删除失败"); |
| | | } |
| | | } catch (error) { |
| | | if (error !== "cancel") { |
| | | console.error("删除失败:", error); |
| | | this.$message.error("删除失败"); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | // 分类型文件上传处理 |
| | | handleDetailFileChange(file, fileList) { |
| | | this.editForm.detailAttachments = fileList; |
| | | // 文件上传处理 |
| | | handleAttachmentChange(file, fileList) { |
| | | this.editForm.attachmentFiles = fileList; |
| | | }, |
| | | |
| | | handleSummaryFileChange(file, fileList) { |
| | | this.editForm.summaryAttachments = fileList; |
| | | }, |
| | | |
| | | handleMinutesFileChange(file, fileList) { |
| | | this.editForm.minutesAttachments = fileList; |
| | | handleMinutesAttachmentChange(file, fileList) { |
| | | this.editForm.recordattachmentFiles = fileList; |
| | | }, |
| | | |
| | | // 删除已上传的文件 |
| | | handleRemoveDetailFile(index) { |
| | | this.editForm.detailAttachments.splice(index, 1); |
| | | handleRemoveAttachmentFile(index) { |
| | | this.editForm.attachmentFiles.splice(index, 1); |
| | | }, |
| | | |
| | | handleRemoveSummaryFile(index) { |
| | | this.editForm.summaryAttachments.splice(index, 1); |
| | | }, |
| | | |
| | | // 文件预览和下载 |
| | | handlePreview(file) { |
| | | // 实现文件预览逻辑 |
| | | this.$message.info(`预览文件: ${file.name}`); |
| | | }, |
| | | |
| | | // 文件下载 |
| | | handleDownload(file) { |
| | | // 实现文件下载逻辑 |
| | | const link = document.createElement("a"); |
| | | link.href = file.url; |
| | | link.download = file.name; |
| | | link.click(); |
| | | this.$message.success(`开始下载: ${file.name}`); |
| | | if (file.url) { |
| | | const link = document.createElement("a"); |
| | | link.href = file.url; |
| | | link.download = file.name; |
| | | link.click(); |
| | | this.$message.success(`开始下载: ${file.name}`); |
| | | } else { |
| | | this.$message.warning("文件地址不存在"); |
| | | } |
| | | }, |
| | | |
| | | // 保存记录 |
| | | async handleSave() { |
| | | try { |
| | | const valid = await this.$refs.editForm.validate(); |
| | | if (!valid) return; |
| | | |
| | | this.saveLoading = true; |
| | | // 模拟API调用 |
| | | await new Promise(resolve => setTimeout(resolve, 1000)); |
| | | // 验证时间 |
| | | if (this.editForm.startTime && this.editForm.endTime) { |
| | | if ( |
| | | new Date(this.editForm.endTime) <= new Date(this.editForm.startTime) |
| | | ) { |
| | | this.$message.error("结束时间必须晚于开始时间"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | this.$message.success(this.isEditing ? "保存成功" : "新增成功"); |
| | | this.editDialogVisible = false; |
| | | this.loadData(); |
| | | this.saveLoading = true; |
| | | |
| | | // 处理附件 |
| | | const formData = { ...this.editForm }; |
| | | |
| | | if (formData.attachmentFiles) { |
| | | formData.attachment = JSON.stringify( |
| | | formData.attachmentFiles.map(file => ({ |
| | | name: file.name, |
| | | url: file.url || "", |
| | | size: file.size || 0, |
| | | type: file.type || file.name.split(".").pop() |
| | | })) |
| | | ); |
| | | } |
| | | |
| | | if (formData.recordattachmentFiles) { |
| | | formData.recordattachment = JSON.stringify( |
| | | formData.recordattachmentFiles.map(file => ({ |
| | | name: file.name, |
| | | url: file.url || "", |
| | | size: file.size || 0, |
| | | type: file.type || file.name.split(".").pop() |
| | | })) |
| | | ); |
| | | } |
| | | |
| | | // 清理文件列表字段 |
| | | 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.$message.error(this.isEditing ? "保存失败" : "新增失败"); |
| | | } finally { |
| | | this.saveLoading = false; |
| | | } |
| | | }, |
| | | |
| | | // 文件上传处理 |
| | | handleFileChange(file, fileList) { |
| | | this.editForm.attachments = fileList; |
| | | }, |
| | | |
| | | // 关闭详情对话框 |
| | |
| | | handleEditClose() { |
| | | this.editDialogVisible = false; |
| | | this.currentRecord = {}; |
| | | this.editForm = this.getDefaultFormData(); |
| | | this.$nextTick(() => { |
| | | 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.pagination.pageNum = 1; |
| | | this.loadData(); |
| | | }, |
| | | |
| | | // 当前页变化 |
| | | handleCurrentChange(page) { |
| | | this.pagination.currentPage = page; |
| | | this.pagination.pageNum = page; |
| | | this.loadData(); |
| | | }, |
| | | |
| | |
| | | console.log("排序变化:", sort); |
| | | }, |
| | | |
| | | // 更新默认表单数据 |
| | | // 获取默认表单数据 |
| | | getDefaultFormData() { |
| | | return { |
| | | id: null, |
| | | title: "", |
| | | meetingType: "", |
| | | participants: [], |
| | | location: "", |
| | | typeId: null, |
| | | locationId: null, |
| | | startTime: "", |
| | | endTime: "", |
| | | summary: "", |
| | | content: "", |
| | | meetingMinutes: "", |
| | | minutesCreateTime: "", |
| | | minutesCreator: "", |
| | | detailAttachments: [], |
| | | summaryAttachments: [], |
| | | minutesAttachments: [] |
| | | attachment: null, |
| | | attachmentFiles: [], |
| | | status: 1, // 默认待开始 |
| | | isRecurring: 0, |
| | | recurringPattern: null, |
| | | parentMeetingId: null, |
| | | reminderMinutes: 30, |
| | | recordcontent: "", |
| | | recordattachment: null, |
| | | recordattachmentFiles: [], |
| | | recorderBy: "", |
| | | remark: "", |
| | | approvalStatus: 0 // 默认待审核 |
| | | }; |
| | | } |
| | | } |
| | |
| | | .minutes-meta { |
| | | border-top: 1px solid #e4e7ed; |
| | | padding-top: 12px; |
| | | } |
| | | |
| | | /* 会议纪要文本区域样式 */ |
| | | .minutes-textarea { |
| | | font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace; |
| | | font-size: 13px; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | /* 响应式调整 */ |
| | |
| | | |
| | | .file-icon { |
| | | font-size: 24px; |
| | | color: #409EFF; |
| | | color: #409eff; |
| | | margin-right: 10px; |
| | | } |
| | | |