会议的参会人员改为用户管理的数据选择及其他页面的回显
| | |
| | | <template> |
| | | <div> |
| | | |
| | | <!-- 申请类型选择 --> |
| | | <el-card class="type-card"> |
| | | <div class="type-selector"> |
| | | <div |
| | | v-for="type in applicationTypes" |
| | | <div v-for="type in applicationTypes" |
| | | :key="type.value" |
| | | class="type-item" |
| | | :class="{ active: currentType === type.value }" |
| | | @click="changeType(type.value)" |
| | | > |
| | | @click="changeType(type.value)"> |
| | | <div class="type-icon"> |
| | | <el-icon :size="24"><component :is="type.icon"/></el-icon> |
| | | <el-icon :size="24"> |
| | | <component :is="type.icon" /> |
| | | </el-icon> |
| | | </div> |
| | | <div class="type-info"> |
| | | <div class="type-name">{{ type.name }}</div> |
| | |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 会议申请表单 --> |
| | | <el-card> |
| | | <div class="form-header"> |
| | | <h3>{{ getCurrentTypeName() }}申请</h3> |
| | | </div> |
| | | |
| | | <el-form |
| | | ref="meetingFormRef" |
| | | <el-form ref="meetingFormRef" |
| | | :model="meetingForm" |
| | | :rules="rules" |
| | | label-width="100px" |
| | | > |
| | | label-width="100px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="会议主题" prop="title"> |
| | | <el-input v-model="meetingForm.title" placeholder="请输入会议主题"/> |
| | | <el-form-item label="会议主题" |
| | | prop="title"> |
| | | <el-input v-model="meetingForm.title" |
| | | placeholder="请输入会议主题" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="会议室" prop="roomId"> |
| | | <el-select v-model="meetingForm.roomId" placeholder="请选择会议室" style="width: 100%"> |
| | | <el-option |
| | | v-for="room in meetingRooms" |
| | | <el-form-item label="会议室" |
| | | prop="roomId"> |
| | | <el-select v-model="meetingForm.roomId" |
| | | placeholder="请选择会议室" |
| | | style="width: 100%"> |
| | | <el-option v-for="room in meetingRooms" |
| | | :key="room.id" |
| | | :label="`${room.name} (${room.location})`" |
| | | :value="room.id" |
| | | /> |
| | | :value="room.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="主持人" prop="host"> |
| | | <el-input v-model="meetingForm.host" placeholder="请输入主持人姓名"/> |
| | | <el-form-item label="主持人" |
| | | prop="host"> |
| | | <el-input v-model="meetingForm.host" |
| | | placeholder="请输入主持人姓名" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="会议日期" prop="meetingDate"> |
| | | <el-date-picker |
| | | v-model="meetingForm.meetingDate" |
| | | <el-form-item label="会议日期" |
| | | prop="meetingDate"> |
| | | <el-date-picker v-model="meetingForm.meetingDate" |
| | | type="date" |
| | | placeholder="请选择会议日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | :disabled-date="disabledDate" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <!-- 空列,保持布局 --> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="开始时间" prop="startTime"> |
| | | <el-select |
| | | v-model="meetingForm.startTime" |
| | | <el-form-item label="开始时间" |
| | | prop="startTime"> |
| | | <el-select v-model="meetingForm.startTime" |
| | | placeholder="请选择开始时间" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="time in startTimeOptions" |
| | | style="width: 100%"> |
| | | <el-option v-for="time in startTimeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" |
| | | /> |
| | | :value="time.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="结束时间" prop="endTime"> |
| | | <el-select |
| | | v-model="meetingForm.endTime" |
| | | <el-form-item label="结束时间" |
| | | prop="endTime"> |
| | | <el-select v-model="meetingForm.endTime" |
| | | placeholder="请选择结束时间" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="time in endTimeOptions" |
| | | style="width: 100%"> |
| | | <el-option v-for="time in endTimeOptions" |
| | | :key="time.value" |
| | | :label="time.label" |
| | | :value="time.value" |
| | | /> |
| | | :value="time.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-form-item label="参会人员" prop="participants"> |
| | | <el-select |
| | | v-model="meetingForm.participants" |
| | | <el-form-item label="参会人员" |
| | | prop="participants"> |
| | | <el-select v-model="meetingForm.participants" |
| | | multiple |
| | | filterable |
| | | placeholder="请选择参会人员" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="person in employees" |
| | | :key="person.id" |
| | | :label="`${person.staffName}${person.postName ? ` (${person.postName})` : ''}`" |
| | | :value="person.id" |
| | | /> |
| | | style="width: 100%"> |
| | | <el-option v-for="user in users" |
| | | :key="user.userId" |
| | | :label="user.deptNames ? `${user.nickName} (${user.deptNames})` : user.nickName" |
| | | :value="user.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="会议说明" prop="description"> |
| | | <el-input |
| | | v-model="meetingForm.description" |
| | | <el-form-item label="会议说明" |
| | | prop="description"> |
| | | <el-input v-model="meetingForm.description" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请输入会议说明" |
| | | /> |
| | | placeholder="请输入会议说明" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div class="form-footer"> |
| | | <el-button @click="resetForm">重置</el-button> |
| | | <el-button type="primary" @click="submitForm">提交</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">提交</el-button> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, onMounted, computed, watch} from 'vue' |
| | | import {ElMessage} from 'element-plus' |
| | | import {Plus, Document, Promotion, Bell} from '@element-plus/icons-vue' |
| | | import {getRoomEnum, saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js' |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { ref, reactive, onMounted, computed, watch } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { Plus, Document, Promotion, Bell } from "@element-plus/icons-vue"; |
| | | import { |
| | | getRoomEnum, |
| | | saveMeetingApplication, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | // 当前申请类型 |
| | | const currentType = ref('department') // approval: 审批流程, department: 部门级, notification: 通知发布 |
| | | const currentType = ref("department"); // approval: 审批流程, department: 部门级, notification: 通知发布 |
| | | |
| | | // 申请类型选项 |
| | | const applicationTypes = ref([ |
| | | { |
| | | value: 'approval', |
| | | name: '审批流程会议', |
| | | desc: '需要经过多级审批的会议申请', |
| | | icon: Document |
| | | value: "approval", |
| | | name: "审批流程会议", |
| | | desc: "需要经过多级审批的会议申请", |
| | | icon: Document, |
| | | }, |
| | | { |
| | | value: 'department', |
| | | name: '部门级会议', |
| | | desc: '部门内部会议申请流程', |
| | | icon: Promotion |
| | | value: "department", |
| | | name: "部门级会议", |
| | | desc: "部门内部会议申请流程", |
| | | icon: Promotion, |
| | | }, |
| | | { |
| | | value: 'notification', |
| | | name: '会议通知', |
| | | desc: '无需审批直接发布的会议通知', |
| | | icon: Bell |
| | | } |
| | | ]) |
| | | value: "notification", |
| | | name: "会议通知", |
| | | desc: "无需审批直接发布的会议通知", |
| | | icon: Bell, |
| | | }, |
| | | ]); |
| | | |
| | | // 表单数据 |
| | | const meetingForm = reactive({ |
| | | title: '', |
| | | type: '', |
| | | roomId: '', |
| | | host: '', |
| | | meetingDate: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | title: "", |
| | | type: "", |
| | | roomId: "", |
| | | host: "", |
| | | meetingDate: "", |
| | | startTime: "", |
| | | endTime: "", |
| | | participants: [], |
| | | description: '' |
| | | }) |
| | | description: "", |
| | | }); |
| | | |
| | | // 表单引用 |
| | | const meetingFormRef = ref(null) |
| | | const meetingFormRef = ref(null); |
| | | |
| | | // 会议室列表 |
| | | const meetingRooms = ref([]) |
| | | const meetingRooms = ref([]); |
| | | |
| | | // 员工列表 |
| | | const employees = ref([]) |
| | | // 用户列表(系统管理-用户管理) |
| | | const users = ref([]); |
| | | |
| | | // 时间选项(以半小时为间隔) |
| | | const timeOptions = ref([]) |
| | | const timeOptions = ref([]); |
| | | |
| | | const getTimeInMinutes = (time) => { |
| | | if (!time) return -1 |
| | | const [hour, minute] = time.split(':').map(Number) |
| | | return hour * 60 + minute |
| | | } |
| | | const getTimeInMinutes = time => { |
| | | if (!time) return -1; |
| | | const [hour, minute] = time.split(":").map(Number); |
| | | return hour * 60 + minute; |
| | | }; |
| | | |
| | | const isToday = (dateText) => { |
| | | if (!dateText) return false |
| | | const [year, month, day] = dateText.split('-').map(Number) |
| | | const now = new Date() |
| | | return year === now.getFullYear() && month === now.getMonth() + 1 && day === now.getDate() |
| | | } |
| | | const isToday = dateText => { |
| | | if (!dateText) return false; |
| | | const [year, month, day] = dateText.split("-").map(Number); |
| | | const now = new Date(); |
| | | return ( |
| | | year === now.getFullYear() && |
| | | month === now.getMonth() + 1 && |
| | | day === now.getDate() |
| | | ); |
| | | }; |
| | | |
| | | const validateStartTime = (_rule, value, callback) => { |
| | | if (!value) { |
| | | callback() |
| | | return |
| | | callback(); |
| | | return; |
| | | } |
| | | |
| | | if (isToday(meetingForm.meetingDate)) { |
| | | const now = new Date() |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes() |
| | | const now = new Date(); |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes(); |
| | | if (getTimeInMinutes(value) > currentMinutes) { |
| | | callback(new Error('当天开始时间不能晚于当前时间')) |
| | | return |
| | | callback(new Error("当天开始时间不能晚于当前时间")); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | callback() |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | const validateEndTime = (_rule, value, callback) => { |
| | | if (!value || !meetingForm.startTime) { |
| | | callback() |
| | | return |
| | | callback(); |
| | | return; |
| | | } |
| | | |
| | | if (getTimeInMinutes(value) <= getTimeInMinutes(meetingForm.startTime)) { |
| | | callback(new Error('结束时间必须大于开始时间')) |
| | | return |
| | | callback(new Error("结束时间必须大于开始时间")); |
| | | return; |
| | | } |
| | | |
| | | callback() |
| | | } |
| | | callback(); |
| | | }; |
| | | |
| | | // 表单校验规则 |
| | | const rules = { |
| | | title: [{required: true, message: '请输入会议主题', trigger: 'blur'}], |
| | | roomId: [{required: true, message: '请选择会议室', trigger: 'change'}], |
| | | host: [{required: true, message: '请输入主持人', trigger: 'blur'}], |
| | | meetingDate: [{required: true, message: '请选择会议日期', trigger: 'change'}], |
| | | title: [{ required: true, message: "请输入会议主题", trigger: "blur" }], |
| | | roomId: [{ required: true, message: "请选择会议室", trigger: "change" }], |
| | | host: [{ required: true, message: "请输入主持人", trigger: "blur" }], |
| | | meetingDate: [ |
| | | { required: true, message: "请选择会议日期", trigger: "change" }, |
| | | ], |
| | | startTime: [ |
| | | {required: true, message: '请选择开始时间', trigger: 'change'}, |
| | | {validator: validateStartTime, trigger: 'change'} |
| | | { required: true, message: "请选择开始时间", trigger: "change" }, |
| | | { validator: validateStartTime, trigger: "change" }, |
| | | ], |
| | | endTime: [ |
| | | {required: true, message: '请选择结束时间', trigger: 'change'}, |
| | | {validator: validateEndTime, trigger: 'change'} |
| | | { required: true, message: "请选择结束时间", trigger: "change" }, |
| | | { validator: validateEndTime, trigger: "change" }, |
| | | ], |
| | | participants: [{required: true, message: '请选择参会人员', trigger: 'change'}] |
| | | } |
| | | participants: [ |
| | | { required: true, message: "请选择参会人员", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | const startTimeOptions = computed(() => { |
| | | if (!isToday(meetingForm.meetingDate)) { |
| | | return timeOptions.value |
| | | return timeOptions.value; |
| | | } |
| | | const now = new Date() |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes() |
| | | return timeOptions.value.filter(item => getTimeInMinutes(item.value) <= currentMinutes) |
| | | }) |
| | | const now = new Date(); |
| | | const currentMinutes = now.getHours() * 60 + now.getMinutes(); |
| | | return timeOptions.value.filter( |
| | | item => getTimeInMinutes(item.value) <= currentMinutes |
| | | ); |
| | | }); |
| | | |
| | | const endTimeOptions = computed(() => { |
| | | if (!meetingForm.startTime) { |
| | | return timeOptions.value |
| | | return timeOptions.value; |
| | | } |
| | | const startMinutes = getTimeInMinutes(meetingForm.startTime) |
| | | return timeOptions.value.filter(item => getTimeInMinutes(item.value) > startMinutes) |
| | | }) |
| | | const startMinutes = getTimeInMinutes(meetingForm.startTime); |
| | | return timeOptions.value.filter( |
| | | item => getTimeInMinutes(item.value) > startMinutes |
| | | ); |
| | | }); |
| | | |
| | | // 初始化时间选项 |
| | | const initTimeOptions = () => { |
| | | const options = [] |
| | | const now = new Date() |
| | | const currentHour = now.getHours() |
| | | const currentMinute = now.getMinutes() |
| | | const options = []; |
| | | const now = new Date(); |
| | | const currentHour = now.getHours(); |
| | | const currentMinute = now.getMinutes(); |
| | | // meetingDate 是 "yyyy-MM-dd" |
| | | const meetingDate = new Date(meetingForm.meetingDate) |
| | | const meetingDate = new Date(meetingForm.meetingDate); |
| | | |
| | | const isSameDay = |
| | | now.getFullYear() === meetingDate.getFullYear() && |
| | | now.getMonth() === meetingDate.getMonth() && |
| | | now.getDate() === meetingDate.getDate() |
| | | now.getDate() === meetingDate.getDate(); |
| | | |
| | | console.log('是否同一天:', isSameDay) |
| | | console.log("是否同一天:", isSameDay); |
| | | for (let hour = 8; hour <= 18; hour++) { |
| | | // 开始时间必须晚于当前时间 |
| | | if (hour < currentHour && isSameDay) { |
| | | continue |
| | | continue; |
| | | } |
| | | if (hour === currentHour && currentMinute > 30 && isSameDay) { |
| | | continue |
| | | continue; |
| | | } |
| | | // 每个小时添加两个选项:整点和半点 |
| | | options.push({ |
| | | value: `${hour.toString().padStart(2, '0')}:00`, |
| | | label: `${hour.toString().padStart(2, '0')}:00` |
| | | }) |
| | | value: `${hour.toString().padStart(2, "0")}:00`, |
| | | label: `${hour.toString().padStart(2, "0")}:00`, |
| | | }); |
| | | |
| | | if (hour < 18) { // 18:00之后没有半点选项 |
| | | if (hour < 18) { |
| | | // 18:00之后没有半点选项 |
| | | options.push({ |
| | | value: `${hour.toString().padStart(2, '0')}:30`, |
| | | label: `${hour.toString().padStart(2, '0')}:30` |
| | | }) |
| | | value: `${hour.toString().padStart(2, "0")}:30`, |
| | | label: `${hour.toString().padStart(2, "0")}:30`, |
| | | }); |
| | | } |
| | | } |
| | | timeOptions.value = options |
| | | } |
| | | timeOptions.value = options; |
| | | }; |
| | | |
| | | watch(() => meetingForm.meetingDate, () => { |
| | | if (meetingForm.startTime && !startTimeOptions.value.some(item => item.value === meetingForm.startTime)) { |
| | | meetingForm.startTime = '' |
| | | watch( |
| | | () => meetingForm.meetingDate, |
| | | () => { |
| | | if ( |
| | | meetingForm.startTime && |
| | | !startTimeOptions.value.some(item => item.value === meetingForm.startTime) |
| | | ) { |
| | | meetingForm.startTime = ""; |
| | | } |
| | | if (meetingForm.endTime && !endTimeOptions.value.some(item => item.value === meetingForm.endTime)) { |
| | | meetingForm.endTime = '' |
| | | if ( |
| | | meetingForm.endTime && |
| | | !endTimeOptions.value.some(item => item.value === meetingForm.endTime) |
| | | ) { |
| | | meetingForm.endTime = ""; |
| | | } |
| | | if (meetingForm.startTime) { |
| | | meetingFormRef.value?.validateField('startTime') |
| | | meetingFormRef.value?.validateField("startTime"); |
| | | } |
| | | if (meetingForm.endTime) { |
| | | meetingFormRef.value?.validateField('endTime') |
| | | meetingFormRef.value?.validateField("endTime"); |
| | | } |
| | | initTimeOptions() |
| | | }) |
| | | initTimeOptions(); |
| | | } |
| | | ); |
| | | |
| | | watch(() => meetingForm.startTime, () => { |
| | | if (meetingForm.endTime && getTimeInMinutes(meetingForm.endTime) <= getTimeInMinutes(meetingForm.startTime)) { |
| | | meetingForm.endTime = '' |
| | | watch( |
| | | () => meetingForm.startTime, |
| | | () => { |
| | | if ( |
| | | meetingForm.endTime && |
| | | getTimeInMinutes(meetingForm.endTime) <= |
| | | getTimeInMinutes(meetingForm.startTime) |
| | | ) { |
| | | meetingForm.endTime = ""; |
| | | } |
| | | if (meetingForm.endTime) { |
| | | meetingFormRef.value?.validateField('endTime') |
| | | meetingFormRef.value?.validateField("endTime"); |
| | | } |
| | | |
| | | }) |
| | | } |
| | | ); |
| | | |
| | | // 禁用日期(禁用今天之前的日期) |
| | | const disabledDate = (time) => { |
| | | const disabledDate = time => { |
| | | // 禁用今天之前的日期 |
| | | return time.getTime() < Date.now() - 86400000 |
| | | } |
| | | return time.getTime() < Date.now() - 86400000; |
| | | }; |
| | | |
| | | // 切换申请类型 |
| | | const changeType = (type) => { |
| | | currentType.value = type |
| | | } |
| | | const changeType = type => { |
| | | currentType.value = type; |
| | | }; |
| | | |
| | | // 获取当前类型名称 |
| | | const getCurrentTypeName = () => { |
| | | const type = applicationTypes.value.find(t => t.value === currentType.value) |
| | | return type ? type.name : '' |
| | | } |
| | | const type = applicationTypes.value.find(t => t.value === currentType.value); |
| | | return type ? type.name : ""; |
| | | }; |
| | | |
| | | // 重置表单 |
| | | const resetForm = () => { |
| | | meetingFormRef.value?.resetFields() |
| | | } |
| | | meetingFormRef.value?.resetFields(); |
| | | }; |
| | | |
| | | // 提交表单 |
| | | const submitForm = () => { |
| | | meetingFormRef.value?.validate((valid) => { |
| | | meetingFormRef.value?.validate(valid => { |
| | | if (valid) { |
| | | |
| | | let formData = {...meetingForm} |
| | | formData.applicationType = currentType.value |
| | | formData.startTime = `${meetingForm.meetingDate} ${meetingForm.startTime}:00` |
| | | formData.endTime = `${meetingForm.meetingDate} ${meetingForm.endTime}:00` |
| | | formData.participants = JSON.stringify(formData.participants) |
| | | console.log(formData) |
| | | let formData = { ...meetingForm }; |
| | | formData.applicationType = currentType.value; |
| | | formData.startTime = `${meetingForm.meetingDate} ${meetingForm.startTime}:00`; |
| | | formData.endTime = `${meetingForm.meetingDate} ${meetingForm.endTime}:00`; |
| | | formData.participants = JSON.stringify(formData.participants); |
| | | console.log(formData); |
| | | saveMeetingApplication(formData).then(() => { |
| | | |
| | | // 模拟提交操作 |
| | | ElMessage.success(`${getCurrentTypeName()}提交成功`) |
| | | ElMessage.success(`${getCurrentTypeName()}提交成功`); |
| | | |
| | | // 根据不同类型执行不同操作 |
| | | switch (currentType.value) { |
| | | case 'approval': |
| | | ElMessage.info('会议已提交审批流程') |
| | | break |
| | | case 'department': |
| | | ElMessage.info('部门级会议申请已提交') |
| | | break |
| | | case 'notification': |
| | | ElMessage.info('会议通知已发布') |
| | | break |
| | | case "approval": |
| | | ElMessage.info("会议已提交审批流程"); |
| | | break; |
| | | case "department": |
| | | ElMessage.info("部门级会议申请已提交"); |
| | | break; |
| | | case "notification": |
| | | ElMessage.info("会议通知已发布"); |
| | | break; |
| | | } |
| | | resetForm() |
| | | }) |
| | | |
| | | resetForm(); |
| | | }); |
| | | } |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 页面加载时初始化 |
| | | onMounted(() => { |
| | | initTimeOptions() |
| | | initTimeOptions(); |
| | | getRoomEnum().then(res => { |
| | | meetingRooms.value = res.data |
| | | }) |
| | | staffOnJobListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | staffState: 1 |
| | | }).then(res => { |
| | | employees.value = res.data.records.sort((a, b) => (a.postName || '').localeCompare(b.postName || '')) |
| | | }) |
| | | }) |
| | | meetingRooms.value = res.data; |
| | | }); |
| | | userListNoPageByTenantId().then(res => { |
| | | const list = Array.isArray(res?.data) ? res.data : []; |
| | | users.value = list |
| | | .map(item => ({ |
| | | userId: item?.userId, |
| | | nickName: |
| | | item?.nickName || item?.userName || String(item?.userId ?? ""), |
| | | deptNames: item?.deptNames || "", |
| | | })) |
| | | .filter( |
| | | item => |
| | | item.userId !== null && item.userId !== undefined && item.nickName |
| | | ) |
| | | .sort((a, b) => String(a.nickName).localeCompare(String(b.nickName))); |
| | | }); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <template> |
| | | <div> |
| | | |
| | | <el-form :model="searchForm" inline> |
| | | <el-form :model="searchForm" |
| | | inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable/> |
| | | <el-input v-model="searchForm.title" |
| | | placeholder="请输入会议主题" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable/> |
| | | <el-input v-model="searchForm.applicant" |
| | | placeholder="请输入申请人" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="审批状态"> |
| | | <el-select style="width: 100px" v-model="searchForm.status" placeholder="请选择审批状态" clearable> |
| | | <el-option label="待审批" value="0"/> |
| | | <el-option label="已通过" value="1"/> |
| | | <el-option label="未审批" value="2"/> |
| | | <el-option label="已取消" value="3"/> |
| | | <el-select style="width: 100px" |
| | | v-model="searchForm.status" |
| | | placeholder="请选择审批状态" |
| | | clearable> |
| | | <el-option label="待审批" |
| | | value="0" /> |
| | | <el-option label="已通过" |
| | | value="1" /> |
| | | <el-option label="未审批" |
| | | value="2" /> |
| | | <el-option label="已取消" |
| | | value="3" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- 会议审批列表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="approvalList" border :height="tableHeight"> |
| | | <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip/> |
| | | <el-table-column prop="applicant" label="申请人" align="center" width="120"/> |
| | | <el-table-column prop="host" label="主理人" align="center" width="120"/> |
| | | <el-table-column prop="meetingTime" label="会议时间" align="center" width="180"> |
| | | <el-table v-loading="loading" |
| | | :data="approvalList" |
| | | border |
| | | :height="tableHeight"> |
| | | <el-table-column prop="title" |
| | | label="会议主题" |
| | | align="center" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" |
| | | label="申请人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="host" |
| | | label="主理人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="meetingTime" |
| | | label="会议时间" |
| | | align="center" |
| | | width="180"> |
| | | <template #default="scope"> |
| | | {{ formatDateTime(scope.row.meetingTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="会议地点" align="center" width="150"/> |
| | | <el-table-column prop="participants" label="参会人数" align="center" width="100"> |
| | | <el-table-column prop="location" |
| | | label="会议地点" |
| | | align="center" |
| | | width="150" /> |
| | | <el-table-column prop="participants" |
| | | label="参会人数" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="审批状态" align="center" width="120"> |
| | | <el-table-column prop="status" |
| | | label="审批状态" |
| | | align="center" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="200" fixed="right"> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | v-if="scope.row.status == '0'" |
| | | <el-button type="primary" |
| | | link |
| | | @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button v-if="scope.row.status == '0'" |
| | | type="primary" |
| | | link |
| | | @click="handleApproval(scope.row)" |
| | | > |
| | | @click="handleApproval(scope.row)"> |
| | | 审批 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 会议详情对话框 --> |
| | | <el-dialog |
| | | title="会议详情" |
| | | <el-dialog title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | width="800px"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions label-width="100px" class="meeting-desc" :column="2" border> |
| | | <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{ |
| | | <el-descriptions label-width="100px" |
| | | class="meeting-desc" |
| | | :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="主理人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.host |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2" |
| | | label-class-name="nowrap-label"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="会议地点" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="参会人数" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.participants.length |
| | | }}人</el-descriptions-item> |
| | | <el-descriptions-item label="审批状态" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="审批状态" |
| | | label-class-name="nowrap-label"> |
| | | <el-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请时间" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2" |
| | | <el-descriptions-item style="max-height: 400px" |
| | | label="会议说明" |
| | | :span="2" |
| | | label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 会议审批对话框 --> |
| | | <el-dialog |
| | | title="会议审批" |
| | | v-model="approvalDialogVisible" |
| | | > |
| | | <el-dialog title="会议审批" |
| | | v-model="approvalDialogVisible"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人">{{ currentMeeting.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-show="false" class="approval-opinion mt-20"> |
| | | <div v-show="false" |
| | | class="approval-opinion mt-20"> |
| | | <h4>审批意见</h4> |
| | | <el-input |
| | | v-model="approvalOpinion" |
| | | <el-input v-model="approvalOpinion" |
| | | type="textarea" |
| | | placeholder="请输入审批意见" |
| | | :rows="4" |
| | | /> |
| | | :rows="4" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="approvalDialogVisible = false">取 消</el-button> |
| | | <el-button type="danger" @click="submitApproval('2')">不通过</el-button> |
| | | <el-button type="primary" @click="submitApproval('1')">通 过</el-button> |
| | | <el-button type="danger" |
| | | @click="submitApproval('2')">不通过</el-button> |
| | | <el-button type="primary" |
| | | @click="submitApproval('1')">通 过</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, onMounted} from 'vue' |
| | | import {ElMessage, ElMessageBox} from 'element-plus' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import {getRoomEnum, getExamineList,saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js' |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import { |
| | | getRoomEnum, |
| | | getExamineList, |
| | | saveMeetingApplication, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import dayjs from "dayjs"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | // 数据列表加载状态 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | |
| | | // 总条数 |
| | | const total = ref(0) |
| | | const total = ref(0); |
| | | |
| | | // 表格高度(根据窗口高度自适应) |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | const tableHeight = ref(window.innerHeight - 380); |
| | | const roomEnum = ref([]); |
| | | const userList = ref([]); |
| | | // 审批列表数据 |
| | | const approvalList = ref([]) |
| | | const approvalList = ref([]); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | size: 10, |
| | | }); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false) |
| | | const approvalDialogVisible = ref(false) |
| | | const detailDialogVisible = ref(false); |
| | | const approvalDialogVisible = ref(false); |
| | | |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null) |
| | | const currentMeeting = ref(null); |
| | | |
| | | // 审批意见 |
| | | const approvalOpinion = ref('') |
| | | const approvalOpinion = ref(""); |
| | | |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getExamineList({...searchForm, ...queryParams}) |
| | | loading.value = true; |
| | | let resp = await getExamineList({ ...searchForm, ...queryParams }); |
| | | const userMap = new Map(userList.value.map(u => [String(u.userId), u])); |
| | | approvalList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.size |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id); |
| | | it.location = `${room.name}(${room.location})`; |
| | | let participantIds = []; |
| | | try { |
| | | participantIds = Array.isArray(it.participants) |
| | | ? it.participants |
| | | : JSON.parse(it.participants); |
| | | } catch (_e) { |
| | | participantIds = []; |
| | | } |
| | | if (!Array.isArray(participantIds)) participantIds = []; |
| | | it.staffCount = participantIds.length; |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format( |
| | | "HH:mm:ss" |
| | | )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`; |
| | | it.participants = participantIds.map(id => { |
| | | const user = userMap.get(String(id)); |
| | | const nickName = user?.nickName || user?.userName || String(id ?? ""); |
| | | const deptNames = user?.deptNames || ""; |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}${staff.postName ? ` (${staff.postName})` : ''}` |
| | | } |
| | | }) |
| | | id, |
| | | name: deptNames ? `${nickName} (${deptNames})` : nickName, |
| | | }; |
| | | }); |
| | | |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | return it; |
| | | }); |
| | | total.value = resp.data.total; |
| | | loading.value = false; |
| | | }; |
| | | |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1 |
| | | getList() |
| | | } |
| | | queryParams.pageNum = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | const viewDetail = row => { |
| | | currentMeeting.value = row; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 处理审批 |
| | | const handleApproval = (row) => { |
| | | currentMeeting.value = row |
| | | approvalOpinion.value = '' |
| | | approvalDialogVisible.value = true |
| | | } |
| | | const handleApproval = row => { |
| | | currentMeeting.value = row; |
| | | approvalOpinion.value = ""; |
| | | approvalDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | '0': 'info', // 待审批 |
| | | '1': 'success', // 已通过 |
| | | '2': 'warning', // 未通过 |
| | | '3': 'danger' // 取消 |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 0: "info", // 待审批 |
| | | 1: "success", // 已通过 |
| | | 2: "warning", // 未通过 |
| | | 3: "danger", // 取消 |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | '0': '待审批', |
| | | '1': '已通过', |
| | | '2': '未通过', |
| | | '3': '已取消' |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | 0: "待审批", |
| | | 1: "已通过", |
| | | 2: "未通过", |
| | | 3: "已取消", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | const formatDateTime = dateTime => { |
| | | if (!dateTime) return ""; |
| | | return dateTime.replace(" ", "\n"); |
| | | }; |
| | | |
| | | // 提交审批 |
| | | const submitApproval = (status) => { |
| | | const submitApproval = status => { |
| | | // if (status === 'approved' && !approvalOpinion.value.trim()) { |
| | | // ElMessage.warning('请填写审批意见') |
| | | // return |
| | | // } |
| | | |
| | | ElMessageBox.confirm( |
| | | `确认${status === '1' ? '通过' : '不通过'}该会议申请?`, |
| | | '审批确认', |
| | | `确认${status === "1" ? "通过" : "不通过"}该会议申请?`, |
| | | "审批确认", |
| | | { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | } |
| | | ).then(() => { |
| | | ) |
| | | .then(() => { |
| | | saveMeetingApplication({ |
| | | id: currentMeeting.value.id, |
| | | status: status |
| | | status: status, |
| | | }).then(resp=>{ |
| | | // 更新会议状态 |
| | | currentMeeting.value.status = status |
| | | currentMeeting.value.status = status; |
| | | |
| | | ElMessage.success('审批提交成功') |
| | | approvalDialogVisible.value = false |
| | | getList() |
| | | ElMessage.success("审批提交成功"); |
| | | approvalDialogVisible.value = false; |
| | | getList(); |
| | | }); |
| | | }) |
| | | |
| | | }).catch(() => { |
| | | }) |
| | | } |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2]= await Promise.all([getRoomEnum(), staffOnJobListPage({current: -1, size: -1, staffState: 1})]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data.records |
| | | const [resp1, resp2] = await Promise.all([ |
| | | getRoomEnum(), |
| | | userListNoPageByTenantId(), |
| | | ]); |
| | | roomEnum.value = resp1.data; |
| | | const list = Array.isArray(resp2?.data) ? resp2.data : []; |
| | | userList.value = list |
| | | .map(item => ({ |
| | | userId: item?.userId, |
| | | nickName: item?.nickName, |
| | | userName: item?.userName, |
| | | deptNames: |
| | | item?.deptNames || (item?.dept?.deptName ? item.dept.deptName : ""), |
| | | })) |
| | | .filter(item => item.userId !== null && item.userId !== undefined); |
| | | |
| | | await getList() |
| | | }) |
| | | await getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <template> |
| | | <div> |
| | | |
| | | <el-form :model="searchForm" inline> |
| | | <el-form :model="searchForm" |
| | | inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable/> |
| | | <el-input v-model="searchForm.title" |
| | | placeholder="请输入会议主题" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable/> |
| | | <el-input v-model="searchForm.applicant" |
| | | placeholder="请输入申请人" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="发布状态"> |
| | | <el-select style="width: 100px" v-model="searchForm.status" placeholder="请选择发布状态" clearable> |
| | | <el-option label="待发布" value="0"/> |
| | | <el-option label="已发布" value="1"/> |
| | | <el-select style="width: 100px" |
| | | v-model="searchForm.status" |
| | | placeholder="请选择发布状态" |
| | | clearable> |
| | | <el-option label="待发布" |
| | | value="0" /> |
| | | <el-option label="已发布" |
| | | value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- 会议发布列表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="approvalList" border :height="tableHeight"> |
| | | <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip/> |
| | | <el-table-column prop="applicant" label="申请人" align="center" width="120"/> |
| | | <el-table-column prop="host" label="主理人" align="center" width="120"/> |
| | | <el-table-column prop="meetingTime" label="会议时间" align="center" width="180"> |
| | | <el-table v-loading="loading" |
| | | :data="approvalList" |
| | | border |
| | | :height="tableHeight"> |
| | | <el-table-column prop="title" |
| | | label="会议主题" |
| | | align="center" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" |
| | | label="申请人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="host" |
| | | label="主理人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="meetingTime" |
| | | label="会议时间" |
| | | align="center" |
| | | width="180"> |
| | | <template #default="scope"> |
| | | {{ formatDateTime(scope.row.meetingTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="会议地点" align="center" width="150"/> |
| | | <el-table-column prop="participants" label="参会人数" align="center" width="100"> |
| | | <el-table-column prop="location" |
| | | label="会议地点" |
| | | align="center" |
| | | width="150" /> |
| | | <el-table-column prop="participants" |
| | | label="参会人数" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="发布状态" align="center" width="120"> |
| | | <el-table-column prop="status" |
| | | label="发布状态" |
| | | align="center" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="200" fixed="right"> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | v-if="scope.row.status == '0'" |
| | | <el-button type="primary" |
| | | link |
| | | @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button v-if="scope.row.status == '0'" |
| | | type="primary" |
| | | link |
| | | @click="handleApproval(scope.row)" |
| | | > |
| | | @click="handleApproval(scope.row)"> |
| | | 发布 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 会议详情对话框 --> |
| | | <el-dialog |
| | | title="会议详情" |
| | | <el-dialog title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | width="800px"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions label-width="100px" class="meeting-desc" :column="2" border> |
| | | <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{ |
| | | <el-descriptions label-width="100px" |
| | | class="meeting-desc" |
| | | :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="主理人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.host |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2" |
| | | label-class-name="nowrap-label"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="会议地点" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="参会人数" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.participants.length |
| | | }}人</el-descriptions-item> |
| | | <el-descriptions-item label="发布状态" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="发布状态" |
| | | label-class-name="nowrap-label"> |
| | | <el-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请时间" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2" |
| | | <el-descriptions-item style="max-height: 400px" |
| | | label="会议说明" |
| | | :span="2" |
| | | label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 会议发布对话框 --> |
| | | <el-dialog |
| | | title="会议发布" |
| | | v-model="approvalDialogVisible" |
| | | > |
| | | <el-dialog title="会议发布" |
| | | v-model="approvalDialogVisible"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item> |
| | | <el-descriptions-item label="主理人">{{ currentMeeting.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="approval-opinion mt-20"> |
| | | <h4>发布意见</h4> |
| | | <el-input |
| | | v-model="publishComment" |
| | | <el-input v-model="publishComment" |
| | | type="textarea" |
| | | placeholder="请输入发布意见" |
| | | :rows="4" |
| | | /> |
| | | :rows="4" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="approvalDialogVisible = false">取 消</el-button> |
| | | <!-- <el-button type="danger" @click="submitApproval('2')">不通过</el-button>--> |
| | | <el-button type="primary" @click="submitApproval('1')">发 布</el-button> |
| | | <el-button type="primary" |
| | | @click="submitApproval('1')">发 布</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, onMounted} from 'vue' |
| | | import {ElMessage, ElMessageBox} from 'element-plus' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import {getRoomEnum, getMeetingPublish,saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js' |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import { |
| | | getRoomEnum, |
| | | getMeetingPublish, |
| | | saveMeetingApplication, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import dayjs from "dayjs"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | // 数据列表加载状态 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | |
| | | // 总条数 |
| | | const total = ref(0) |
| | | const total = ref(0); |
| | | |
| | | // 表格高度(根据窗口高度自适应) |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | const tableHeight = ref(window.innerHeight - 380); |
| | | const roomEnum = ref([]); |
| | | const userList = ref([]); |
| | | // 发布列表数据 |
| | | const approvalList = ref([]) |
| | | const approvalList = ref([]); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | size: 10, |
| | | }); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false) |
| | | const approvalDialogVisible = ref(false) |
| | | const detailDialogVisible = ref(false); |
| | | const approvalDialogVisible = ref(false); |
| | | |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null) |
| | | const currentMeeting = ref(null); |
| | | |
| | | // 发布意见 |
| | | const publishComment = ref('') |
| | | const publishComment = ref(""); |
| | | |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getMeetingPublish({...searchForm, ...queryParams}) |
| | | loading.value = true; |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }); |
| | | const userMap = new Map(userList.value.map(u => [String(u.userId), u])); |
| | | approvalList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.size |
| | | it.status = it.publishStatus |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id); |
| | | it.location = `${room.name}(${room.location})`; |
| | | let participantIds = []; |
| | | try { |
| | | participantIds = Array.isArray(it.participants) |
| | | ? it.participants |
| | | : JSON.parse(it.participants); |
| | | } catch (_e) { |
| | | participantIds = []; |
| | | } |
| | | if (!Array.isArray(participantIds)) participantIds = []; |
| | | it.staffCount = participantIds.length; |
| | | it.status = it.publishStatus; |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format( |
| | | "HH:mm:ss" |
| | | )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`; |
| | | it.participants = participantIds.map(id => { |
| | | const user = userMap.get(String(id)); |
| | | const nickName = user?.nickName || user?.userName || String(id ?? ""); |
| | | const deptNames = user?.deptNames || ""; |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}${staff.postName ? ` (${staff.postName})` : ''}` |
| | | } |
| | | }) |
| | | id, |
| | | name: deptNames ? `${nickName} (${deptNames})` : nickName, |
| | | }; |
| | | }); |
| | | |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | return it; |
| | | }); |
| | | total.value = resp.data.total; |
| | | loading.value = false; |
| | | }; |
| | | |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1 |
| | | getList() |
| | | } |
| | | queryParams.pageNum = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | title: "", |
| | | applicant: "", |
| | | status: "", |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | const viewDetail = row => { |
| | | currentMeeting.value = row; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 处理发布 |
| | | const handleApproval = (row) => { |
| | | currentMeeting.value = row |
| | | publishComment.value = '' |
| | | approvalDialogVisible.value = true |
| | | } |
| | | const handleApproval = row => { |
| | | currentMeeting.value = row; |
| | | publishComment.value = ""; |
| | | approvalDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | '0': 'info', // 待发布 |
| | | '1': 'success', // 已通过 |
| | | '2': 'danger', // 未通过 |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 0: "info", // 待发布 |
| | | 1: "success", // 已通过 |
| | | 2: "danger", // 未通过 |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | '0': '待发布', |
| | | '1': '已发布', |
| | | '2': '已取消', |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | 0: "待发布", |
| | | 1: "已发布", |
| | | 2: "已取消", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | const formatDateTime = dateTime => { |
| | | if (!dateTime) return ""; |
| | | return dateTime.replace(" ", "\n"); |
| | | }; |
| | | |
| | | // 提交发布 |
| | | const submitApproval = (status) => { |
| | | const submitApproval = status => { |
| | | // if (status === 'approved' && !publishComment.value.trim()) { |
| | | // ElMessage.warning('请填写发布意见') |
| | | // return |
| | | // } |
| | | |
| | | ElMessageBox.confirm( |
| | | `确认${status === '1' ? '发布' : '取消'}该会议?`, |
| | | '发布确认', |
| | | `确认${status === "1" ? "发布" : "取消"}该会议?`, |
| | | "发布确认", |
| | | { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | } |
| | | ).then(() => { |
| | | ) |
| | | .then(() => { |
| | | saveMeetingApplication({ |
| | | id: currentMeeting.value.id, |
| | | publishStatus: status, |
| | | publishComment: publishComment.value |
| | | publishComment: publishComment.value, |
| | | }).then(resp=>{ |
| | | // 更新会议状态 |
| | | currentMeeting.value.status = status |
| | | currentMeeting.value.status = status; |
| | | |
| | | ElMessage.success('发布提交成功') |
| | | approvalDialogVisible.value = false |
| | | getList() |
| | | ElMessage.success("发布提交成功"); |
| | | approvalDialogVisible.value = false; |
| | | getList(); |
| | | }); |
| | | }) |
| | | |
| | | }).catch(() => { |
| | | }) |
| | | } |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2]= await Promise.all([getRoomEnum(), staffOnJobListPage({current: -1, size: -1, staffState: 1})]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data.records |
| | | const [resp1, resp2] = await Promise.all([ |
| | | getRoomEnum(), |
| | | userListNoPageByTenantId(), |
| | | ]); |
| | | roomEnum.value = resp1.data; |
| | | const list = Array.isArray(resp2?.data) ? resp2.data : []; |
| | | userList.value = list |
| | | .map(item => ({ |
| | | userId: item?.userId, |
| | | nickName: item?.nickName, |
| | | userName: item?.userName, |
| | | deptNames: |
| | | item?.deptNames || (item?.dept?.deptName ? item.dept.deptName : ""), |
| | | })) |
| | | .filter(item => item.userId !== null && item.userId !== undefined); |
| | | |
| | | await getList() |
| | | }) |
| | | await getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <template> |
| | | <div> |
| | | |
| | | <el-form :model="searchForm" inline> |
| | | <el-form :model="searchForm" |
| | | inline> |
| | | <el-form-item label="会议主题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入会议主题" clearable /> |
| | | <el-input v-model="searchForm.title" |
| | | placeholder="请输入会议主题" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="申请人"> |
| | | <el-input v-model="searchForm.applicant" placeholder="请输入申请人" clearable /> |
| | | <el-input v-model="searchForm.applicant" |
| | | placeholder="请输入申请人" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">搜索</el-button> |
| | | <el-button type="primary" |
| | | @click="handleSearch">搜索</el-button> |
| | | <el-button @click="resetSearch">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- 会议列表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="meetingList" border :height="tableHeight"> |
| | | <el-table-column prop="title" label="会议主题" align="center" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" label="申请人" align="center" width="120" /> |
| | | <el-table-column prop="host" label="主持人" align="center" width="120" /> |
| | | <el-table-column prop="meetingTime" label="会议时间" align="center" width="180"> |
| | | <el-table v-loading="loading" |
| | | :data="meetingList" |
| | | border |
| | | :height="tableHeight"> |
| | | <el-table-column prop="title" |
| | | label="会议主题" |
| | | align="center" |
| | | min-width="200" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="applicant" |
| | | label="申请人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="host" |
| | | label="主持人" |
| | | align="center" |
| | | width="120" /> |
| | | <el-table-column prop="meetingTime" |
| | | label="会议时间" |
| | | align="center" |
| | | width="180"> |
| | | <template #default="scope"> |
| | | {{ formatDateTime(scope.row.meetingTime) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="会议地点" align="center" width="150" /> |
| | | <el-table-column prop="participants" label="参会人数" align="center" width="100"> |
| | | <el-table-column prop="location" |
| | | label="会议地点" |
| | | align="center" |
| | | width="150" /> |
| | | <el-table-column prop="participants" |
| | | label="参会人数" |
| | | align="center" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" align="center" width="200" fixed="right"> |
| | | <el-table-column label="操作" |
| | | align="center" |
| | | width="200" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | type="primary" |
| | | <el-button type="primary" |
| | | link |
| | | @click="addMinutes(scope.row)" |
| | | > |
| | | @click="viewDetail(scope.row)">查看</el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="addMinutes(scope.row)"> |
| | | 添加纪要 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- 分页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | <pagination v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | @pagination="getList" /> |
| | | </el-card> |
| | | |
| | | <!-- 会议详情对话框 --> |
| | | <el-dialog |
| | | title="会议详情" |
| | | <el-dialog title="会议详情" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | width="800px"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions label-width="100px" class="meeting-desc" :column="2" border> |
| | | <el-descriptions-item label="会议主题" label-class-name="nowrap-label">{{ |
| | | <el-descriptions label-width="100px" |
| | | class="meeting-desc" |
| | | :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="主持人" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="主持人" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.host |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2" |
| | | label-class-name="nowrap-label"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="会议地点" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="参会人数" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.participants.length |
| | | }}人</el-descriptions-item> |
| | | <el-descriptions-item label="审批状态" label-class-name="nowrap-label"> |
| | | <el-descriptions-item label="审批状态" |
| | | label-class-name="nowrap-label"> |
| | | <el-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请时间" label-class-name="nowrap-label">{{ |
| | | <el-descriptions-item label="申请时间" |
| | | label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item style="max-height: 400px" label="会议说明" :span="2" |
| | | <el-descriptions-item style="max-height: 400px" |
| | | label="会议说明" |
| | | :span="2" |
| | | label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>参会人员</h4> |
| | | <div class="participants-list"> |
| | | <el-tag |
| | | v-for="participant in currentMeeting.participants" |
| | | <el-tag v-for="participant in currentMeeting.participants" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | style="margin-right: 10px; margin-bottom: 10px;"> |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 添加会议纪要对话框 --> |
| | | <el-dialog |
| | | title="添加会议纪要" |
| | | <el-dialog title="添加会议纪要" |
| | | v-model="minutesDialogVisible" |
| | | width="80%" |
| | | @close="handleCloseMinutesDialog" |
| | | > |
| | | @close="handleCloseMinutesDialog"> |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions :column="2" |
| | | border> |
| | | <el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item> |
| | | <el-descriptions-item label="主持人">{{ currentMeeting.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="会议时间" :span="2"> |
| | | <el-descriptions-item label="会议时间" |
| | | :span="2"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item> |
| | | <el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>会议纪要内容</h4> |
| | | <div class="editor-container"> |
| | | <Editor |
| | | v-model="minutesContent" |
| | | :min-height="400" |
| | | /> |
| | | <Editor v-model="minutesContent" |
| | | :min-height="400" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="minutesDialogVisible = false">取 消</el-button> |
| | | <el-button type="primary" @click="submitMinutes">保 存</el-button> |
| | | <el-button type="primary" |
| | | @click="submitMinutes">保 存</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import Editor from '@/components/Editor/index.vue' |
| | | import { getRoomEnum, getMeetingPublish ,getMeetingMinutesByMeetingId,saveMeetingMinutes} from '@/api/collaborativeApproval/meeting.js' |
| | | import dayjs from "dayjs" |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import Editor from "@/components/Editor/index.vue"; |
| | | import { |
| | | getRoomEnum, |
| | | getMeetingPublish, |
| | | getMeetingMinutesByMeetingId, |
| | | saveMeetingMinutes, |
| | | } from "@/api/collaborativeApproval/meeting.js"; |
| | | import dayjs from "dayjs"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | // 数据列表加载状态 |
| | | const loading = ref(false) |
| | | const loading = ref(false); |
| | | |
| | | // 总条数 |
| | | const total = ref(0) |
| | | const total = ref(0); |
| | | |
| | | // 表格高度(根据窗口高度自适应) |
| | | const tableHeight = ref(window.innerHeight - 380) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | const tableHeight = ref(window.innerHeight - 380); |
| | | const roomEnum = ref([]); |
| | | const userList = ref([]); |
| | | |
| | | // 会议列表数据 |
| | | const meetingList = ref([]) |
| | | const meetingList = ref([]); |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | size: 10, |
| | | }); |
| | | |
| | | // 搜索表单 |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | title: "", |
| | | applicant: "", |
| | | // status: '1' // 默认只显示已通过审批的会议 |
| | | }) |
| | | }); |
| | | |
| | | // 是否显示对话框 |
| | | const detailDialogVisible = ref(false) |
| | | const minutesDialogVisible = ref(false) |
| | | const detailDialogVisible = ref(false); |
| | | const minutesDialogVisible = ref(false); |
| | | |
| | | // 当前查看的会议 |
| | | const currentMeeting = ref(null) |
| | | const currentMeeting = ref(null); |
| | | |
| | | // 会议纪要内容 |
| | | const minutesContent = ref('') |
| | | const minutesContentId = ref('') |
| | | const minutesContent = ref(""); |
| | | const minutesContentId = ref(""); |
| | | |
| | | const parseParticipants = raw => { |
| | | if (!raw) return []; |
| | | if (Array.isArray(raw)) return raw; |
| | | try { |
| | | const parsed = JSON.parse(raw); |
| | | return Array.isArray(parsed) ? parsed : []; |
| | | } catch (_e) { |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | // 查询数据 |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }) |
| | | loading.value = true; |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }); |
| | | const userMap = new Map(userList.value.map(u => [String(u.userId), u])); |
| | | meetingList.value = resp.data.records.map(it => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id) |
| | | it.location = `${room.name}(${room.location})` |
| | | let staffs = JSON.parse(it.participants) |
| | | it.staffCount = staffs.size |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}` |
| | | it.participants = staffList.value.filter(staff => staffs.some(id => id === staff.id)).map(staff => { |
| | | let room = roomEnum.value.find(room => it.roomId === room.id); |
| | | it.location = `${room.name}(${room.location})`; |
| | | const participantIds = parseParticipants(it.participants); |
| | | it.staffCount = participantIds.length; |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format( |
| | | "HH:mm:ss" |
| | | )} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`; |
| | | it.participants = participantIds.map(id => { |
| | | const user = userMap.get(String(id)); |
| | | const nickName = user?.nickName || user?.userName || String(id ?? ""); |
| | | const deptNames = user?.deptNames || ""; |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}${staff.postName ? ` (${staff.postName})` : ''}` |
| | | } |
| | | }) |
| | | id, |
| | | name: deptNames ? `${nickName} (${deptNames})` : nickName, |
| | | }; |
| | | }); |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | return it; |
| | | }); |
| | | total.value = resp.data.total; |
| | | loading.value = false; |
| | | }; |
| | | |
| | | // 搜索按钮操作 |
| | | const handleSearch = () => { |
| | | queryParams.current = 1 |
| | | getList() |
| | | } |
| | | queryParams.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 重置搜索表单 |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | title: "", |
| | | applicant: "", |
| | | // status: '1' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | }); |
| | | handleSearch(); |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | const viewDetail = row => { |
| | | currentMeeting.value = row; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 添加会议纪要 |
| | | const addMinutes = async (row) => { |
| | | let resp = await getMeetingMinutesByMeetingId(row.id) |
| | | currentMeeting.value = row |
| | | const addMinutes = async row => { |
| | | let resp = await getMeetingMinutesByMeetingId(row.id); |
| | | currentMeeting.value = row; |
| | | if (resp.data){ |
| | | minutesContent.value = resp.data.content |
| | | minutesContentId.value = resp.data.id |
| | | minutesContent.value = resp.data.content; |
| | | minutesContentId.value = resp.data.id; |
| | | }else { |
| | | minutesContent.value = `<h2>${row.title}会议纪要</h2> |
| | | <p><strong>会议时间:</strong>${row.meetingTime}</p> |
| | |
| | | <p><strong>主持人:</strong>${row.host}</p> |
| | | <p><strong>参会人员:</strong></p> |
| | | <ol> |
| | | ${row.participants.map(p => `<li>${p.name}</li>`).join('')} |
| | | ${row.participants.map(p => `<li>${p.name}</li>`).join("")} |
| | | </ol> |
| | | <p><strong>会议内容:</strong></p> |
| | | <ol> |
| | |
| | | </ul> |
| | | </li> |
| | | </ol> |
| | | <p><strong>备注:</strong></p>` |
| | | <p><strong>备注:</strong></p>`; |
| | | } |
| | | |
| | | minutesDialogVisible.value = true |
| | | } |
| | | minutesDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 提交会议纪要 |
| | | const submitMinutes = () => { |
| | | if (!minutesContent.value) { |
| | | ElMessage.warning('请输入会议纪要内容') |
| | | return |
| | | ElMessage.warning("请输入会议纪要内容"); |
| | | return; |
| | | } |
| | | saveMeetingMinutes({ |
| | | id: minutesContentId.value, |
| | | content: minutesContent.value, |
| | | meetingId: currentMeeting.value.id, |
| | | title: currentMeeting.value.title |
| | | title: currentMeeting.value.title, |
| | | }).then(resp=>{ |
| | | console.log('会议纪要内容:', minutesContent.value) |
| | | ElMessage.success('会议纪要保存成功') |
| | | minutesDialogVisible.value = false |
| | | }) |
| | | |
| | | } |
| | | console.log("会议纪要内容:", minutesContent.value); |
| | | ElMessage.success("会议纪要保存成功"); |
| | | minutesDialogVisible.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 关闭会议纪要对话框 |
| | | const handleCloseMinutesDialog = () => { |
| | | minutesContent.value = '' |
| | | } |
| | | minutesContent.value = ""; |
| | | }; |
| | | |
| | | // 获取状态类型 |
| | | const getStatusType = (status) => { |
| | | const getStatusType = status => { |
| | | const statusMap = { |
| | | '0': 'info', // 待审批 |
| | | '1': 'success', // 已通过 |
| | | '2': 'warning', // 未通过 |
| | | '3': 'danger' // 取消 |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | 0: "info", // 待审批 |
| | | 1: "success", // 已通过 |
| | | 2: "warning", // 未通过 |
| | | 3: "danger", // 取消 |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | // 获取状态文本 |
| | | const getStatusText = (status) => { |
| | | const getStatusText = status => { |
| | | const statusMap = { |
| | | '0': '待审批', |
| | | '1': '已通过', |
| | | '2': '未通过', |
| | | '3': '已取消' |
| | | } |
| | | return statusMap[status] || '未知' |
| | | } |
| | | 0: "待审批", |
| | | 1: "已通过", |
| | | 2: "未通过", |
| | | 3: "已取消", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | // 格式化日期时间 |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | const formatDateTime = dateTime => { |
| | | if (!dateTime) return ""; |
| | | return dateTime.replace(" ", "\n"); |
| | | }; |
| | | |
| | | // 页面加载时获取数据 |
| | | onMounted(async () => { |
| | | const [resp1, resp2] = await Promise.all([getRoomEnum(), staffOnJobListPage({current: -1, size: -1, staffState: 1})]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data.records |
| | | const [resp1, resp2] = await Promise.all([ |
| | | getRoomEnum(), |
| | | userListNoPageByTenantId(), |
| | | ]); |
| | | roomEnum.value = resp1.data; |
| | | const list = Array.isArray(resp2?.data) ? resp2.data : []; |
| | | userList.value = list |
| | | .map(item => ({ |
| | | userId: item?.userId, |
| | | nickName: item?.nickName, |
| | | userName: item?.userName, |
| | | deptNames: |
| | | item?.deptNames || (item?.dept?.deptName ? item.dept.deptName : ""), |
| | | })) |
| | | .filter(item => item.userId !== null && item.userId !== undefined); |
| | | |
| | | await getList() |
| | | }) |
| | | await getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |