<template>
|
<div class="app-container">
|
<!-- 页面标题 -->
|
<div class="page-header">
|
<h2>会议申请</h2>
|
</div>
|
|
<!-- 申请类型选择 -->
|
<el-card class="type-card">
|
<div class="type-selector">
|
<div
|
v-for="type in applicationTypes"
|
:key="type.value"
|
class="type-item"
|
:class="{ active: currentType === type.value }"
|
@click="changeType(type.value)"
|
>
|
<div class="type-icon">
|
<el-icon :size="24"><component :is="type.icon"/></el-icon>
|
</div>
|
<div class="type-info">
|
<div class="type-name">{{ type.name }}</div>
|
<div class="type-desc">{{ type.desc }}</div>
|
</div>
|
</div>
|
</div>
|
</el-card>
|
|
<!-- 会议申请表单 -->
|
<el-card>
|
<div class="form-header">
|
<h3>{{ getCurrentTypeName() }}申请</h3>
|
</div>
|
|
<el-form
|
ref="meetingFormRef"
|
:model="meetingForm"
|
:rules="rules"
|
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>
|
</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"
|
:key="room.id"
|
:label="`${room.name} (${room.location})`"
|
: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>
|
</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"
|
type="date"
|
placeholder="请选择会议日期"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
:disabled-date="disabledDate"
|
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"
|
placeholder="请选择开始时间"
|
style="width: 100%"
|
>
|
<el-option
|
v-for="time in timeOptions"
|
:key="time.value"
|
:label="time.label"
|
: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"
|
placeholder="请选择结束时间"
|
style="width: 100%"
|
>
|
<el-option
|
v-for="time in timeOptions"
|
:key="time.value"
|
:label="time.label"
|
:value="time.value"
|
/>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<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.postJob})`"
|
:value="person.id"
|
/>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="会议说明" prop="description">
|
<el-input
|
v-model="meetingForm.description"
|
type="textarea"
|
:rows="4"
|
placeholder="请输入会议说明"
|
/>
|
</el-form-item>
|
</el-form>
|
|
<div class="form-footer">
|
<el-button @click="resetForm">重置</el-button>
|
<el-button type="primary" @click="submitForm">提交</el-button>
|
</div>
|
</el-card>
|
</div>
|
</template>
|
|
<script setup>
|
import {ref, reactive, onMounted} 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 {getStaffOnJob} from "@/api/personnelManagement/onboarding.js";
|
|
// 当前申请类型
|
const currentType = ref('department') // approval: 审批流程, department: 部门级, notification: 通知发布
|
|
// 申请类型选项
|
const applicationTypes = ref([
|
{
|
value: 'approval',
|
name: '审批流程会议',
|
desc: '需要经过多级审批的会议申请',
|
icon: Document
|
},
|
{
|
value: 'department',
|
name: '部门级会议',
|
desc: '部门内部会议申请流程',
|
icon: Promotion
|
},
|
{
|
value: 'notification',
|
name: '会议通知',
|
desc: '无需审批直接发布的会议通知',
|
icon: Bell
|
}
|
])
|
|
// 表单数据
|
const meetingForm = reactive({
|
title: '',
|
type: '',
|
roomId: '',
|
host: '',
|
meetingDate: '',
|
startTime: '',
|
endTime: '',
|
participants: [],
|
description: ''
|
})
|
|
// 表单校验规则
|
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'}],
|
startTime: [{required: true, message: '请选择开始时间', trigger: 'change'}],
|
endTime: [{required: true, message: '请选择结束时间', trigger: 'change'}],
|
participants: [{required: true, message: '请选择参会人员', trigger: 'change'}]
|
}
|
|
// 表单引用
|
const meetingFormRef = ref(null)
|
|
// 会议室列表
|
const meetingRooms = ref([])
|
|
// 员工列表
|
const employees = ref([])
|
|
// 时间选项(以半小时为间隔)
|
const timeOptions = ref([])
|
|
// 初始化时间选项
|
const initTimeOptions = () => {
|
const options = []
|
for (let hour = 8; hour <= 18; hour++) {
|
// 每个小时添加两个选项:整点和半点
|
options.push({
|
value: `${hour.toString().padStart(2, '0')}:00`,
|
label: `${hour.toString().padStart(2, '0')}:00`
|
})
|
|
if (hour < 18) { // 18:00之后没有半点选项
|
options.push({
|
value: `${hour.toString().padStart(2, '0')}:30`,
|
label: `${hour.toString().padStart(2, '0')}:30`
|
})
|
}
|
}
|
timeOptions.value = options
|
}
|
|
// 禁用日期(禁用今天之前的日期)
|
const disabledDate = (time) => {
|
// 禁用今天之前的日期
|
return time.getTime() < Date.now() - 86400000
|
}
|
|
// 切换申请类型
|
const changeType = (type) => {
|
currentType.value = type
|
}
|
|
// 获取当前类型名称
|
const getCurrentTypeName = () => {
|
const type = applicationTypes.value.find(t => t.value === currentType.value)
|
return type ? type.name : ''
|
}
|
|
// 重置表单
|
const resetForm = () => {
|
meetingFormRef.value?.resetFields()
|
}
|
|
// 提交表单
|
const submitForm = () => {
|
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)
|
saveMeetingApplication(formData).then(() => {
|
|
// 模拟提交操作
|
ElMessage.success(`${getCurrentTypeName()}提交成功`)
|
|
// 根据不同类型执行不同操作
|
switch (currentType.value) {
|
case 'approval':
|
ElMessage.info('会议已提交审批流程')
|
break
|
case 'department':
|
ElMessage.info('部门级会议申请已提交')
|
break
|
case 'notification':
|
ElMessage.info('会议通知已发布')
|
break
|
}
|
resetForm()
|
})
|
|
}
|
})
|
}
|
|
// 页面加载时初始化
|
onMounted(() => {
|
initTimeOptions()
|
getRoomEnum().then(res => {
|
meetingRooms.value = res.data
|
})
|
getStaffOnJob().then(res => {
|
employees.value = res.data.sort((a, b) => a.postJob.localeCompare(b.postJob))
|
})
|
})
|
</script>
|
|
<style scoped>
|
.app-container {
|
padding: 20px;
|
}
|
|
.page-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
}
|
|
.page-header h2 {
|
margin: 0;
|
color: #303133;
|
}
|
|
.type-card {
|
margin-bottom: 20px;
|
}
|
|
.type-selector {
|
display: flex;
|
gap: 20px;
|
}
|
|
.type-item {
|
flex: 1;
|
display: flex;
|
align-items: center;
|
padding: 20px;
|
border: 1px solid #ebeef5;
|
border-radius: 8px;
|
cursor: pointer;
|
transition: all 0.3s;
|
}
|
|
.type-item:hover {
|
border-color: #409eff;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
}
|
|
.type-item.active {
|
border-color: #409eff;
|
background-color: #ecf5ff;
|
}
|
|
.type-icon {
|
margin-right: 15px;
|
color: #409eff;
|
}
|
|
.type-name {
|
font-size: 16px;
|
font-weight: 500;
|
color: #303133;
|
margin-bottom: 5px;
|
}
|
|
.type-desc {
|
font-size: 14px;
|
color: #909399;
|
}
|
|
.form-header {
|
margin-bottom: 20px;
|
padding-bottom: 15px;
|
border-bottom: 1px solid #ebeef5;
|
}
|
|
.form-header h3 {
|
margin: 0;
|
color: #303133;
|
}
|
|
.form-footer {
|
display: flex;
|
justify-content: flex-end;
|
gap: 10px;
|
margin-top: 30px;
|
padding-top: 20px;
|
border-top: 1px solid #ebeef5;
|
}
|
</style>
|