<template>
|
<div class="meeting-management">
|
<!-- 页面头部 -->
|
<div class="page-header">
|
<div class="header-actions">
|
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">
|
新建会议
|
</el-button>
|
<el-button icon="el-icon-download" @click="exportData">
|
导出数据
|
</el-button>
|
</div>
|
</div>
|
|
<!-- 搜索筛选区域 -->
|
<el-card class="filter-card">
|
<el-form :model="queryParams" inline>
|
<el-form-item label="会议类型">
|
<el-select
|
v-model="queryParams.meetingType"
|
clearable
|
placeholder="请选择"
|
>
|
<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="会议地点">
|
<el-input
|
v-model="queryParams.location"
|
placeholder="请输入会议地点"
|
clearable
|
style="width: 150px"
|
/>
|
</el-form-item>
|
<el-form-item label="会议开始时间范围">
|
<el-date-picker
|
v-model="queryParams.dateRange"
|
type="daterange"
|
range-separator="至"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
value-format="yyyy-MM-dd"
|
/>
|
</el-form-item>
|
<el-form-item label="状态">
|
<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>
|
<el-button @click="handleReset">重置</el-button>
|
</el-form-item>
|
</el-form>
|
</el-card>
|
|
<!-- 数据表格 -->
|
<el-card>
|
<el-table
|
:data="tableData"
|
v-loading="loading"
|
border
|
style="width: 100%"
|
@sort-change="handleSortChange"
|
>
|
<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.id)">
|
{{ scope.row.title }}
|
</el-button>
|
</template>
|
</el-table-column>
|
<el-table-column
|
align="center"
|
prop="meetingType"
|
label="会议类型"
|
width="120"
|
>
|
<template #default="scope">
|
<el-tag :type="getMeetingTypeTag(scope.row.typeId)">
|
{{ getMeetingTypeText(scope.row.typeId) }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column
|
align="center"
|
label="会议状态"
|
width="100"
|
fixed="right"
|
>
|
<template #default="scope">
|
<el-tag :type="getMeetingStatusTag(scope.row.status)">
|
{{ getMeetingStatusText(scope.row.status) }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<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
|
align="center"
|
prop="endTime"
|
label="结束时间"
|
width="160"
|
sortable
|
>
|
<template #default="scope">
|
<span>{{ formatDateTime(scope.row.endTime) }}</span>
|
</template>
|
</el-table-column>
|
<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
|
align="center"
|
prop="summary"
|
label="会议概要"
|
min-width="200"
|
show-overflow-tooltip
|
/>
|
<el-table-column align="center" label="会议纪要" width="120">
|
<template #default="scope">
|
<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="操作" align="center" width="200" fixed="right">
|
<template #default="scope">
|
<el-button
|
size="mini"
|
type="text"
|
@click="handleView(scope.row.id)"
|
>
|
查看
|
</el-button>
|
<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.id)"
|
style="color: #67C23A;"
|
:disabled="scope.row.delFlag === 1"
|
>
|
复制
|
</el-button>
|
<el-button
|
size="mini"
|
type="text"
|
@click="handleDelete(scope.row)"
|
style="color: #F56C6C;"
|
:disabled="scope.row.delFlag === 1"
|
>
|
删除
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<!-- 分页 -->
|
<div class="pagination-container">
|
<el-pagination
|
: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
|
:title="`会议详情 - ${currentRecord.title || ''}`"
|
:visible.sync="detailDialogVisible"
|
width="800px"
|
:before-close="handleDetailClose"
|
>
|
<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.typeId)">
|
{{ getMeetingTypeText(currentRecord.typeId) }}
|
</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="会议状态">
|
<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="结束时间">{{
|
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
|
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="创建人">{{
|
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.id)"
|
:disabled="currentRecord.delFlag === 1"
|
>
|
编辑
|
</el-button>
|
</span>
|
</el-dialog>
|
|
<!-- 新增/编辑对话框 -->
|
<el-dialog
|
:title="`${isEditing ? '编辑' : '新增'}会议`"
|
:visible.sync="editDialogVisible"
|
width="700px"
|
:before-close="handleEditClose"
|
>
|
<el-form
|
ref="editForm"
|
:model="editForm"
|
:rules="editRules"
|
label-width="100px"
|
label-position="left"
|
>
|
<el-form-item label="会议主题" prop="title">
|
<el-input v-model="editForm.title" placeholder="请输入会议主题" />
|
</el-form-item>
|
|
<el-form-item label="会议类型" prop="typeId">
|
<el-select
|
v-model="editForm.typeId"
|
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-option label="评审会议" :value="5" />
|
</el-select>
|
</el-form-item>
|
|
<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 :span="12">
|
<el-form-item label="开始时间" prop="startTime">
|
<el-date-picker
|
v-model="editForm.startTime"
|
type="datetime"
|
placeholder="选择开始时间"
|
value-format="yyyy-MM-dd HH:mm:ss"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="结束时间" prop="endTime">
|
<el-date-picker
|
v-model="editForm.endTime"
|
type="datetime"
|
placeholder="选择结束时间"
|
value-format="yyyy-MM-dd HH:mm:ss"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</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"
|
type="textarea"
|
:rows="2"
|
placeholder="请输入会议概要"
|
maxlength="200"
|
show-word-limit
|
/>
|
</el-form-item>
|
|
<el-form-item label="会议内容" prop="content">
|
<el-input
|
v-model="editForm.content"
|
type="textarea"
|
:rows="6"
|
placeholder="请输入会议具体内容(支持HTML格式)"
|
/>
|
</el-form-item>
|
|
<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 ? "保存" : "新增" }}
|
</el-button>
|
</span>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import {
|
meetinglist,
|
meetingedit,
|
meetingadd,
|
meetingInfo,
|
meetingDel,
|
exporremeeting
|
} from "@/api/officeManagementApi";
|
|
export default {
|
name: "MeetingManagement",
|
data() {
|
return {
|
// 查询参数
|
queryParams: {
|
title: "",
|
meetingType: "",
|
location: "",
|
dateRange: [],
|
status: ""
|
},
|
// 分页参数
|
pagination: {
|
pageNum: 1,
|
pageSize: 10,
|
total: 0
|
},
|
// 加载状态
|
loading: false,
|
saveLoading: false,
|
// 对话框显示状态
|
detailDialogVisible: false,
|
editDialogVisible: false,
|
minutesDialogVisible: false,
|
// 当前操作记录
|
currentRecord: {},
|
// 编辑状态
|
isEditing: false,
|
// 表格数据
|
tableData: [],
|
// 编辑表单数据
|
editForm: this.getDefaultFormData(),
|
// 表单验证规则
|
editRules: {
|
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();
|
},
|
methods: {
|
// 加载数据
|
async loadData() {
|
this.loading = true;
|
try {
|
const params = {
|
pageNum: this.pagination.pageNum,
|
pageSize: this.pagination.pageSize,
|
...this.queryParams
|
};
|
|
// 处理时间范围查询
|
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("数据加载失败");
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
// 获取文件图标
|
getFileIcon(fileName) {
|
if (!fileName) return "el-icon-document";
|
const ext = fileName
|
.split(".")
|
.pop()
|
.toLowerCase();
|
const iconMap = {
|
pdf: "el-icon-document",
|
doc: "el-icon-document",
|
docx: "el-icon-document",
|
xls: "el-icon-document",
|
xlsx: "el-icon-document",
|
ppt: "el-icon-document",
|
pptx: "el-icon-document",
|
jpg: "el-icon-picture",
|
jpeg: "el-icon-picture",
|
png: "el-icon-picture",
|
gif: "el-icon-picture",
|
zip: "el-icon-folder",
|
rar: "el-icon-folder"
|
};
|
return iconMap[ext] || "el-icon-document";
|
},
|
|
// 查看会议纪要
|
handleViewMinutes(record) {
|
this.currentRecord = { ...record };
|
this.minutesDialogVisible = true;
|
},
|
|
// 解析附件JSON
|
parseAttachments(attachmentJson) {
|
if (!attachmentJson) return [];
|
try {
|
if (typeof attachmentJson === "string") {
|
return JSON.parse(attachmentJson);
|
}
|
return attachmentJson;
|
} catch (error) {
|
console.error("解析附件失败:", error);
|
return [];
|
}
|
},
|
|
// 获取会议类型标签样式
|
getMeetingTypeTag(typeId) {
|
const typeMap = {
|
1: "primary", // 科研会议
|
2: "success", // 日常会议
|
3: "warning", // 项目会议
|
4: "info", // 部门会议
|
5: "danger" // 评审会议
|
};
|
return typeMap[typeId] || "info";
|
},
|
|
// 获取会议类型文本
|
getMeetingTypeText(typeId) {
|
const textMap = {
|
1: "科研会议",
|
2: "日常会议",
|
3: "项目会议",
|
4: "部门会议",
|
5: "评审会议"
|
};
|
return textMap[typeId] || "其他";
|
},
|
|
// 获取会议状态标签样式
|
getMeetingStatusTag(status) {
|
const statusMap = {
|
1: "primary", // 待开始
|
2: "success", // 进行中
|
3: "info", // 已结束
|
4: "danger" // 已取消
|
};
|
return statusMap[status] || "info";
|
},
|
|
// 获取会议状态文本
|
getMeetingStatusText(status) {
|
const textMap = {
|
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 "";
|
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 (duration < 60) {
|
return `${Math.round(duration)}分钟`;
|
} else {
|
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.pageNum = 1;
|
this.loadData();
|
},
|
|
// 重置查询
|
handleReset() {
|
this.queryParams = {
|
title: "",
|
meetingType: "",
|
location: "",
|
dateRange: [],
|
status: ""
|
};
|
this.pagination.pageNum = 1;
|
this.loadData();
|
},
|
|
// 查看详情
|
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.$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("获取记录失败");
|
}
|
},
|
|
// 复制记录
|
async handleCopy(id) {
|
try {
|
const response = await meetingInfo(id);
|
if (response.code === 200) {
|
this.isEditing = false;
|
const copiedRecord = { ...response.data };
|
|
// 移除ID,生成新记录
|
delete copiedRecord.id;
|
delete copiedRecord.meetingNumber;
|
copiedRecord.title = copiedRecord.title + "(复制)";
|
copiedRecord.parentMeetingId = id;
|
|
// 解析附件
|
if (copiedRecord.attachment) {
|
copiedRecord.attachmentFiles = this.parseAttachments(
|
copiedRecord.attachment
|
);
|
}
|
if (copiedRecord.recordattachment) {
|
copiedRecord.recordattachmentFiles = this.parseAttachments(
|
copiedRecord.recordattachment
|
);
|
}
|
|
this.editForm = copiedRecord;
|
this.editDialogVisible = true;
|
this.$nextTick(() => {
|
this.$refs.editForm && this.$refs.editForm.clearValidate();
|
});
|
} else {
|
this.$message.error(response.msg || "获取记录失败");
|
}
|
} catch (error) {
|
console.error("获取记录失败:", error);
|
this.$message.error("获取记录失败");
|
}
|
},
|
|
// 删除记录
|
async handleDelete(record) {
|
try {
|
await this.$confirm("确定要删除这条会议记录吗?", "提示", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning"
|
});
|
|
const response = await meetingDel(record.id);
|
if (response.code === 200) {
|
this.$message.success("删除成功");
|
this.loadData();
|
} else {
|
this.$message.error(response.msg || "删除失败");
|
}
|
} catch (error) {
|
if (error !== "cancel") {
|
console.error("删除失败:", error);
|
this.$message.error("删除失败");
|
}
|
}
|
},
|
|
// 文件上传处理
|
handleAttachmentChange(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;
|
|
// 验证时间
|
if (this.editForm.startTime && this.editForm.endTime) {
|
if (
|
new Date(this.editForm.endTime) <= new Date(this.editForm.startTime)
|
) {
|
this.$message.error("结束时间必须晚于开始时间");
|
return;
|
}
|
}
|
|
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.isEditing ? "保存失败" : "新增失败");
|
} finally {
|
this.saveLoading = false;
|
}
|
},
|
|
// 关闭详情对话框
|
handleDetailClose() {
|
this.detailDialogVisible = false;
|
this.currentRecord = {};
|
},
|
|
// 关闭编辑对话框
|
handleEditClose() {
|
this.editDialogVisible = false;
|
this.currentRecord = {};
|
this.editForm = this.getDefaultFormData();
|
this.$nextTick(() => {
|
this.$refs.editForm && this.$refs.editForm.clearValidate();
|
});
|
},
|
|
// 导出数据
|
exportData() {
|
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.pageNum = 1;
|
this.loadData();
|
},
|
|
// 当前页变化
|
handleCurrentChange(page) {
|
this.pagination.pageNum = page;
|
this.loadData();
|
},
|
|
// 排序变化
|
handleSortChange(sort) {
|
console.log("排序变化:", sort);
|
},
|
|
// 获取默认表单数据
|
getDefaultFormData() {
|
return {
|
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>
|
.meeting-management {
|
padding: 20px;
|
}
|
|
.page-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
}
|
|
.page-header h2 {
|
margin: 0;
|
color: #303133;
|
}
|
|
.filter-card {
|
margin-bottom: 20px;
|
}
|
|
.pagination-container {
|
margin-top: 20px;
|
display: flex;
|
justify-content: flex-end;
|
}
|
|
.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 {
|
flex-direction: column;
|
align-items: flex-start;
|
gap: 10px;
|
}
|
|
.header-actions {
|
width: 100%;
|
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>
|