| | |
| | | <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" /> |
| | |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="状态"> |
| | | <el-select v-model="queryParams.status" clearable placeholder="请选择"> |
| | | <el-select |
| | | v-model="queryParams.status" |
| | | clearable |
| | | placeholder="请选择" |
| | | > |
| | | <el-option label="待开始" value="pending" /> |
| | | <el-option label="进行中" value="ongoing" /> |
| | | <el-option label="已结束" value="completed" /> |
| | |
| | | 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 /> --> |
| | | <el-table-column |
| | | prop="title" |
| | | align="center" |
| | | label="会议主题" |
| | | width="200" |
| | | fixed |
| | | > |
| | | <template #default="scope"> |
| | | <el-button type="text" @click="handleView(scope.row)"> |
| | | {{ 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> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="participants" label="参会人员" width="180" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <span>{{ scope.row.participants.join(', ') }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="会议地点" width="150" /> |
| | | <el-table-column 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> |
| | | <template #default="scope"> |
| | | <span>{{ formatDateTime(scope.row.endTime) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column 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="status" |
| | | label="状态" |
| | | width="100" |
| | | fixed="right" |
| | | > |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusTag(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="200" fixed="right"> |
| | | <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> |
| | | <el-table-column |
| | | align="center" |
| | | prop="location" |
| | | 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.meetingMinutes" |
| | | > |
| | | {{ scope.row.meetingMinutes ? "查看纪要" : "暂无" }} |
| | | </el-button> |
| | | </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)"> |
| | | 查看 |
| | |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <el-dialog |
| | | title="会议纪要" |
| | | :visible.sync="minutesDialogVisible" |
| | | width="600px" |
| | | > |
| | | <div v-if="currentRecord.meetingMinutes"> |
| | | <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: 400px; overflow-y: auto;" |
| | | > |
| | | {{ currentRecord.meetingMinutes }} |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | <div |
| | | class="minutes-meta" |
| | | style="margin-top: 16px; color: #909399; font-size: 12px;" |
| | | > |
| | | <span |
| | | >创建时间: |
| | | {{ formatDateTime(currentRecord.minutesCreateTime) }}</span |
| | | > |
| | | <span style="margin-left: 16px;" |
| | | >创建人: {{ currentRecord.minutesCreator || "系统" }}</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)" |
| | | v-if="currentRecord.meetingMinutes" |
| | | > |
| | | 编辑纪要 |
| | | </el-button> |
| | | </span> |
| | | </el-dialog> |
| | | <!-- 查看详情对话框 --> |
| | | <el-dialog |
| | | :title="`会议详情 - ${currentRecord.title || ''}`" |
| | |
| | | :before-close="handleDetailClose" |
| | | > |
| | | <el-descriptions :column="2" border v-if="currentRecord"> |
| | | <el-descriptions-item label="会议主题">{{ currentRecord.title }}</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) }} |
| | |
| | | {{ participant }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点">{{ currentRecord.location }}</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> |
| | | </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="开始时间">{{ |
| | | 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="会议概要" :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;"> |
| | | <div |
| | | style="white-space: pre-line; max-height: 300px; overflow-y: auto;" |
| | | > |
| | | {{ currentRecord.content }} |
| | | </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-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> |
| | | |
| | | <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)" |
| | | >编辑</el-button |
| | | > |
| | | </span> |
| | | </el-dialog> |
| | | |
| | |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="会议类型" prop="meetingType"> |
| | | <el-select v-model="editForm.meetingType" placeholder="请选择会议类型" style="width: 100%"> |
| | | <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" /> |
| | |
| | | multiple |
| | | > |
| | | <el-button size="small" type="primary">点击上传</el-button> |
| | | <div slot="tip" class="el-upload__tip">支持上传文档、图片等文件,单个文件不超过10MB</div> |
| | | <div slot="tip" class="el-upload__tip"> |
| | | 支持上传文档、图片等文件,单个文件不超过10MB |
| | | </div> |
| | | </el-upload> |
| | | </el-form-item> |
| | | <el-form-item label="会议纪要" prop="meetingMinutes"> |
| | | <el-input |
| | | v-model="editForm.meetingMinutes" |
| | | 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> |
| | | |
| | | <span slot="footer"> |
| | | <el-button @click="handleEditClose">取消</el-button> |
| | | <el-button type="primary" @click="handleSave" :loading="saveLoading"> |
| | | {{ isEditing ? '保存' : '新增' }} |
| | | {{ isEditing ? "保存" : "新增" }} |
| | | </el-button> |
| | | </span> |
| | | </el-dialog> |
| | |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'MeetingManagement', |
| | | name: "MeetingManagement", |
| | | data() { |
| | | return { |
| | | // 查询参数 |
| | | queryParams: { |
| | | meetingType: '', |
| | | location: '', |
| | | meetingType: "", |
| | | location: "", |
| | | dateRange: [], |
| | | status: '' |
| | | status: "" |
| | | }, |
| | | // 分页参数 |
| | | pagination: { |
| | |
| | | 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: '吴十' } |
| | | { 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: '', |
| | | title: "", |
| | | meetingType: "", |
| | | participants: [], |
| | | location: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | summary: '', |
| | | content: '', |
| | | location: "", |
| | | startTime: "", |
| | | endTime: "", |
| | | summary: "", |
| | | content: "", |
| | | attachments: [] |
| | | }, |
| | | // 表单验证规则 |
| | | 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" }], |
| | | 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" } |
| | | ], |
| | | meetingMinutes: [ |
| | | { |
| | | max: 1000, |
| | | message: "会议纪要长度不能超过1000个字符", |
| | | 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)) |
| | | await new Promise(resolve => setTimeout(resolve, 500)); |
| | | |
| | | // 生成模拟数据 |
| | | this.tableData = this.generateMockData() |
| | | this.pagination.total = this.tableData.length |
| | | this.tableData = this.generateMockData(); |
| | | this.pagination.total = this.tableData.length; |
| | | } catch (error) { |
| | | console.error('加载数据失败:', error) |
| | | this.$message.error('数据加载失败') |
| | | console.error("加载数据失败:", error); |
| | | this.$message.error("数据加载失败"); |
| | | } finally { |
| | | this.loading = false |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | // 查看会议纪要 |
| | | 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 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 = [] |
| | | 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]) |
| | | const randomIndex = Math.floor( |
| | | Math.random() * participantsPool.length |
| | | ); |
| | | participants.push(participantsPool[randomIndex]); |
| | | } |
| | | |
| | | // 去重 |
| | | const uniqueParticipants = [...new Set(participants)] |
| | | 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 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 |
| | | ); |
| | | |
| | | 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)] |
| | | : ""; |
| | | |
| | | return { |
| | | id: index + 1, |
| | | title: `关于${['科研项目', '日常工作', '技术评审', '部门协调'][Math.floor(Math.random() * 4)]}的会议`, |
| | | meetingType: meetingTypes[Math.floor(Math.random() * meetingTypes.length)], |
| | | title: `关于${ |
| | | ["科研项目", "日常工作", "技术评审", "部门协调"][ |
| | | Math.floor(Math.random() * 4) |
| | | ] |
| | | }的会议`, |
| | | meetingType: |
| | | meetingTypes[Math.floor(Math.random() * meetingTypes.length)], |
| | | participants: uniqueParticipants, |
| | | location: ['第一会议室', '第二会议室', '第三会议室', '线上会议'][Math.floor(Math.random() * 4)], |
| | | location: ["第一会议室", "第二会议室", "第三会议室", "线上会议"][ |
| | | Math.floor(Math.random() * 4) |
| | | ], |
| | | startTime: startTime.toISOString(), |
| | | endTime: endTime.toISOString(), |
| | | summary: `本次会议主要讨论${['项目进展', '技术难题', '工作计划', '问题协调'][Math.floor(Math.random() * 4)]}等相关事宜`, |
| | | 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: '系统管理员', |
| | | creator: "系统管理员", |
| | | attachments: [] |
| | | } |
| | | }) |
| | | }; |
| | | }); |
| | | }, |
| | | |
| | | // 获取会议类型标签样式 |
| | | getMeetingTypeTag(type) { |
| | | const typeMap = { |
| | | research: 'primary', |
| | | daily: 'success', |
| | | project: 'warning', |
| | | department: 'info', |
| | | review: 'danger' |
| | | } |
| | | return typeMap[type] || 'info' |
| | | research: "primary", |
| | | daily: "success", |
| | | project: "warning", |
| | | department: "info", |
| | | review: "danger" |
| | | }; |
| | | return typeMap[type] || "info"; |
| | | }, |
| | | |
| | | // 获取会议类型文本 |
| | | getMeetingTypeText(type) { |
| | | const textMap = { |
| | | research: '科研会议', |
| | | daily: '日常会议', |
| | | project: '项目会议', |
| | | department: '部门会议', |
| | | review: '评审会议' |
| | | } |
| | | return textMap[type] || type |
| | | research: "科研会议", |
| | | daily: "日常会议", |
| | | project: "项目会议", |
| | | department: "部门会议", |
| | | review: "评审会议" |
| | | }; |
| | | return textMap[type] || type; |
| | | }, |
| | | |
| | | // 获取状态标签样式 |
| | | getStatusTag(status) { |
| | | const statusMap = { |
| | | pending: 'primary', |
| | | ongoing: 'success', |
| | | completed: 'info', |
| | | cancelled: 'danger' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | pending: "primary", |
| | | ongoing: "success", |
| | | completed: "info", |
| | | cancelled: "danger" |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }, |
| | | |
| | | // 获取状态文本 |
| | | getStatusText(status) { |
| | | const textMap = { |
| | | pending: '待开始', |
| | | ongoing: '进行中', |
| | | completed: '已结束', |
| | | cancelled: '已取消' |
| | | } |
| | | return textMap[status] || status |
| | | pending: "待开始", |
| | | ongoing: "进行中", |
| | | completed: "已结束", |
| | | cancelled: "已取消" |
| | | }; |
| | | return textMap[status] || status; |
| | | }, |
| | | |
| | | // 格式化日期时间 |
| | | formatDateTime(dateTime) { |
| | | if (!dateTime) return '' |
| | | const date = new Date(dateTime) |
| | | return date.toLocaleString('zh-CN') |
| | | if (!dateTime) return ""; |
| | | const date = new Date(dateTime); |
| | | return date.toLocaleString("zh-CN"); |
| | | }, |
| | | |
| | | // 计算会议持续时间 |
| | | 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}小时`; |
| | | } |
| | | }, |
| | | |
| | | // 查询处理 |
| | | handleQuery() { |
| | | this.pagination.currentPage = 1 |
| | | this.loadData() |
| | | this.pagination.currentPage = 1; |
| | | this.loadData(); |
| | | }, |
| | | |
| | | // 重置查询 |
| | | handleReset() { |
| | | this.queryParams = { |
| | | meetingType: '', |
| | | location: '', |
| | | meetingType: "", |
| | | location: "", |
| | | dateRange: [], |
| | | status: '' |
| | | } |
| | | this.pagination.currentPage = 1 |
| | | this.loadData() |
| | | status: "" |
| | | }; |
| | | this.pagination.currentPage = 1; |
| | | this.loadData(); |
| | | }, |
| | | |
| | | // 查看详情 |
| | | handleView(record) { |
| | | this.currentRecord = { ...record } |
| | | this.detailDialogVisible = true |
| | | this.currentRecord = { ...record }; |
| | | this.detailDialogVisible = true; |
| | | }, |
| | | |
| | | // 新增记录 |
| | | 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.isEditing = true; |
| | | this.currentRecord = record; |
| | | this.editForm = { ...record }; |
| | | this.editDialogVisible = true; |
| | | this.detailDialogVisible = false; |
| | | this.$nextTick(() => { |
| | | this.$refs.editForm && this.$refs.editForm.clearValidate() |
| | | }) |
| | | this.$refs.editForm && this.$refs.editForm.clearValidate(); |
| | | }); |
| | | }, |
| | | |
| | | // 复制记录 |
| | | handleCopy(record) { |
| | | this.isEditing = false |
| | | const copiedRecord = { ...record } |
| | | delete copiedRecord.id |
| | | copiedRecord.title = copiedRecord.title + '(复制)' |
| | | this.editForm = copiedRecord |
| | | this.editDialogVisible = true |
| | | this.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() |
| | | }) |
| | | this.$refs.editForm && this.$refs.editForm.clearValidate(); |
| | | }); |
| | | }, |
| | | |
| | | // 删除记录 |
| | | handleDelete(record) { |
| | | this.$confirm('确定要删除这条会议记录吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | // 模拟删除操作 |
| | | this.tableData = this.tableData.filter(item => item.id !== record.id) |
| | | this.pagination.total = this.tableData.length |
| | | this.$message.success('删除成功') |
| | | }).catch(() => {}) |
| | | this.$confirm("确定要删除这条会议记录吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning" |
| | | }) |
| | | .then(() => { |
| | | // 模拟删除操作 |
| | | this.tableData = this.tableData.filter(item => item.id !== record.id); |
| | | this.pagination.total = this.tableData.length; |
| | | this.$message.success("删除成功"); |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | |
| | | // 保存记录 |
| | | async handleSave() { |
| | | try { |
| | | const valid = await this.$refs.editForm.validate() |
| | | if (!valid) return |
| | | const valid = await this.$refs.editForm.validate(); |
| | | if (!valid) return; |
| | | |
| | | this.saveLoading = true |
| | | this.saveLoading = true; |
| | | // 模拟API调用 |
| | | await new Promise(resolve => setTimeout(resolve, 1000)) |
| | | await new Promise(resolve => setTimeout(resolve, 1000)); |
| | | |
| | | this.$message.success(this.isEditing ? '保存成功' : '新增成功') |
| | | this.editDialogVisible = false |
| | | this.loadData() |
| | | this.$message.success(this.isEditing ? "保存成功" : "新增成功"); |
| | | this.editDialogVisible = false; |
| | | this.loadData(); |
| | | } catch (error) { |
| | | console.error('保存失败:', error) |
| | | this.$message.error('操作失败') |
| | | console.error("保存失败:", error); |
| | | this.$message.error("操作失败"); |
| | | } finally { |
| | | this.saveLoading = false |
| | | this.saveLoading = false; |
| | | } |
| | | }, |
| | | |
| | | // 文件上传处理 |
| | | handleFileChange(file, fileList) { |
| | | this.editForm.attachments = 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.$nextTick(() => { |
| | | this.$refs.editForm && this.$refs.editForm.clearValidate() |
| | | }) |
| | | this.$refs.editForm && this.$refs.editForm.clearValidate(); |
| | | }); |
| | | }, |
| | | |
| | | // 导出数据 |
| | | exportData() { |
| | | this.$message.success('导出功能开发中') |
| | | this.$message.success("导出功能开发中"); |
| | | }, |
| | | |
| | | // 分页大小变化 |
| | | handleSizeChange(size) { |
| | | this.pagination.pageSize = size |
| | | this.pagination.currentPage = 1 |
| | | this.loadData() |
| | | this.pagination.pageSize = size; |
| | | this.pagination.currentPage = 1; |
| | | this.loadData(); |
| | | }, |
| | | |
| | | // 当前页变化 |
| | | handleCurrentChange(page) { |
| | | this.pagination.currentPage = page |
| | | this.loadData() |
| | | this.pagination.currentPage = page; |
| | | this.loadData(); |
| | | }, |
| | | |
| | | // 排序变化 |
| | | handleSortChange(sort) { |
| | | console.log('排序变化:', sort) |
| | | console.log("排序变化:", sort); |
| | | }, |
| | | |
| | | // 获取默认表单数据 |
| | | // 更新默认表单数据 |
| | | getDefaultFormData() { |
| | | return { |
| | | title: '', |
| | | meetingType: '', |
| | | title: "", |
| | | meetingType: "", |
| | | participants: [], |
| | | location: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | summary: '', |
| | | content: '', |
| | | location: "", |
| | | startTime: "", |
| | | endTime: "", |
| | | summary: "", |
| | | content: "", |
| | | meetingMinutes: "", |
| | | minutesCreateTime: "", |
| | | minutesCreator: "", |
| | | attachments: [] |
| | | } |
| | | }; |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | .attachment-item { |
| | | margin-bottom: 8px; |
| | | } |
| | | .minutes-content { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | | /* 响应式调整 */ |
| | | @media (max-width: 768px) { |
| | | .minutes-content .el-card { |
| | | margin: 0 -20px; |
| | | } |
| | | } |
| | | /* 响应式设计 */ |
| | | @media (max-width: 768px) { |
| | | .page-header { |