| | |
| | | <div class="meeting-management"> |
| | | <!-- 页面头部 --> |
| | | <div class="page-header"> |
| | | <h2>会议管理</h2> |
| | | <div class="header-actions"> |
| | | <el-button type="primary" icon="el-icon-plus" @click="handleAdd"> |
| | | 新建会议 |
| | |
| | | <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" /> |
| | |
| | | style="width: 150px" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="时间范围"> |
| | | <el-form-item label="会议开始时间范围"> |
| | | <el-date-picker |
| | | v-model="queryParams.dateRange" |
| | | type="daterange" |
| | |
| | | /> |
| | | </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> |
| | |
| | | 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" |
| | | /> |
| | | <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> |
| | |
| | | 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.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="minutes-attachments" |
| | | v-if=" |
| | | currentRecord.recordattachment && |
| | | parseAttachments(currentRecord.recordattachment).length > 0 |
| | | " |
| | | > |
| | | <el-divider content-position="left">会议纪要附件</el-divider> |
| | | <div class="attachment-list"> |
| | | <div |
| | | v-for="file in parseAttachments(currentRecord.recordattachment)" |
| | | :key="file.id || file.name" |
| | | class="attachment-item" |
| | | > |
| | | <el-link |
| | | type="primary" |
| | | :href="file.url" |
| | | target="_blank" |
| | | class="file-link" |
| | | > |
| | | <i |
| | | :class="getFileIcon(file.type || file.name)" |
| | | style="margin-right: 8px;" |
| | | ></i> |
| | | {{ file.name }} |
| | | <span class="file-size" v-if="file.size" |
| | | >({{ formatFileSize(file.size) }})</span |
| | | > |
| | | </el-link> |
| | | </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 |
| | |
| | | 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-descriptions-item label="会议附件" :span="2"> |
| | | <div class="detail-attachments"> |
| | | <div |
| | | v-if=" |
| | | currentRecord.attachment && |
| | | parseAttachments(currentRecord.attachment).length > 0 |
| | | " |
| | | class="attachment-grid" |
| | | > |
| | | <div |
| | | 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 || 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" 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 |
| | | > |
| | | </div> |
| | | </el-card> |
| | | </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> |
| | | |
| | |
| | | <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="locationId"> |
| | | <el-select |
| | | v-model="editForm.locationId" |
| | | placeholder="请选择会议地点" |
| | | style="width: 100%" |
| | | > |
| | | <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-row :gutter="20"> |
| | |
| | | </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-upload |
| | | action="#" |
| | | :auto-upload="false" |
| | | :on-change="handleFileChange" |
| | | :file-list="editForm.attachments" |
| | | :limit="5" |
| | | multiple |
| | | > |
| | | <el-button size="small" type="primary">点击上传</el-button> |
| | | <div slot="tip" class="el-upload__tip">支持上传文档、图片等文件,单个文件不超过10MB</div> |
| | | </el-upload> |
| | | <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="handleMinutesAttachmentChange" |
| | | :file-list="editForm.recordattachmentFiles" |
| | | :limit="5" |
| | | multiple |
| | | > |
| | | <el-button size="small" type="primary" icon="el-icon-upload"> |
| | | 上传纪要附件 |
| | | </el-button> |
| | | <div slot="tip" class="el-upload__tip"> |
| | | 支持图片、文档等格式,单个文件不超过10MB |
| | | </div> |
| | | </el-upload> |
| | | </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> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { |
| | | meetinglist, |
| | | meetingedit, |
| | | meetingadd, |
| | | meetingInfo, |
| | | meetingDel, |
| | | exporremeeting |
| | | } from "@/api/officeManagementApi"; |
| | | |
| | | export default { |
| | | name: 'MeetingManagement', |
| | | name: "MeetingManagement", |
| | | data() { |
| | | return { |
| | | // 查询参数 |
| | | queryParams: { |
| | | meetingType: '', |
| | | location: '', |
| | | title: "", |
| | | meetingType: "", |
| | | location: "", |
| | | dateRange: [], |
| | | status: '' |
| | | status: "" |
| | | }, |
| | | // 分页参数 |
| | | 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: '吴十' } |
| | | ], |
| | | // 编辑表单数据 |
| | | 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" } |
| | | ] |
| | | } |
| | | } |
| | | }; |
| | | }, |
| | | 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; |
| | | } |
| | | }, |
| | | |
| | | // 生成模拟数据 |
| | | generateMockData() { |
| | | const meetingTypes = ['research', 'daily', 'project', 'department', 'review'] |
| | | const statuses = ['pending', 'ongoing', 'completed', 'cancelled'] |
| | | const participantsPool = ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十'] |
| | | // 获取文件图标 |
| | | 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"; |
| | | }, |
| | | |
| | | 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]) |
| | | // 查看会议纪要 |
| | | 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) 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) { |
| | | this.currentRecord = response.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.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() |
| | | }) |
| | | 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 |
| | | this.$message.success('删除成功') |
| | | }).catch(() => {}) |
| | | 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(file, fileList) { |
| | | this.editForm.attachmentFiles = fileList; |
| | | }, |
| | | |
| | | handleMinutesAttachmentChange(file, fileList) { |
| | | this.editForm.recordattachmentFiles = fileList; |
| | | }, |
| | | |
| | | // 删除已上传的文件 |
| | | handleRemoveAttachmentFile(index) { |
| | | this.editForm.attachmentFiles.splice(index, 1); |
| | | }, |
| | | |
| | | // 文件下载 |
| | | handleDownload(file) { |
| | | 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 |
| | | 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('操作失败') |
| | | console.error("保存失败:", error); |
| | | this.$message.error(this.isEditing ? "保存失败" : "新增失败"); |
| | | } finally { |
| | | this.saveLoading = false |
| | | this.saveLoading = false; |
| | | } |
| | | }, |
| | | |
| | | // 文件上传处理 |
| | | handleFileChange(file, fileList) { |
| | | this.editForm.attachments = fileList |
| | | }, |
| | | |
| | | // 关闭详情对话框 |
| | | 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.$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: null, |
| | | attachmentFiles: [], |
| | | status: 1, // 默认待开始 |
| | | isRecurring: 0, |
| | | recurringPattern: null, |
| | | parentMeetingId: null, |
| | | reminderMinutes: 30, |
| | | recordcontent: "", |
| | | recordattachment: null, |
| | | recordattachmentFiles: [], |
| | | recorderBy: "", |
| | | remark: "", |
| | | approvalStatus: 0 // 默认待审核 |
| | | }; |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | .attachment-item { |
| | | 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 { |
| | |
| | | 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> |