| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | export function getMeetingRoomList(data) { |
| | | return request({ |
| | | url: "/meeting/roomList", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | export function saveRoom(data) { |
| | | return request({ |
| | | url: "/meeting/saveRoom", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | export function delRoom(id) { |
| | | return request({ |
| | | url: "/meeting/delRoom/"+id, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | export function getRoomEnum() { |
| | | return request({ |
| | | url: "/meeting/roomEnum", |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | export function getDraftList(data){ |
| | | return request({ |
| | | url: "/meeting/draftList", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | export function saveDraft(data) { |
| | | return request({ |
| | | url: "/meeting/saveDraft", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | export function delDraft(id) { |
| | | return request({ |
| | | url: "/meeting/delDraft/"+id, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | export function saveMeetingApplication(data){ |
| | | return request({ |
| | | url: "/meeting/saveMeetingApplication", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | export function getExamineList(data) { |
| | | return request({ |
| | | url: "/meeting/applicationList", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | |
| | | export function getMeetingUseList(data){ |
| | | return request({ |
| | | url: "/meeting/meetingUseList", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | export function getMeetingPublish(data){ |
| | | return request({ |
| | | url: "/meeting/meetingPublishList", |
| | | method: "post", |
| | | data: data |
| | | }); |
| | | } |
| | | |
| | | |
| | | export function getMeetingMinutesByMeetingId(id){ |
| | | return request({ |
| | | url: "/meeting/getMeetingMinutesByMeetingId/"+id, |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | export function saveMeetingMinutes(data){ |
| | | return request({ |
| | | url: "/meeting/saveMeetingMinutes", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | |
| | | export function getMeetSummary(){ |
| | | return request({ |
| | | url: "/meeting/getMeetSummary", |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | export function getMeetSummaryItems(){ |
| | | return request({ |
| | | url: "/meeting/getMeetSummaryItems", |
| | | method: "get", |
| | | }); |
| | | } |
| | |
| | | </el-card> |
| | | <el-card class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-number">{{ stats.ongoing }}</div> |
| | | <div class="stat-number">{{ stats.underWay }}</div> |
| | | <div class="stat-label">è¿è¡ä¸</div> |
| | | </div> |
| | | </el-card> |
| | |
| | | </el-card> |
| | | <el-card class="stat-card"> |
| | | <div class="stat-content"> |
| | | <div class="stat-number">{{ stats.upcoming }}</div> |
| | | <div class="stat-number">{{ stats.toStart }}</div> |
| | | <div class="stat-label">å³å°å¼å§</div> |
| | | </div> |
| | | </el-card> |
| | |
| | | </el-tag> |
| | | </div> |
| | | <div class="meeting-time"> |
| | | <el-icon><Clock /></el-icon> |
| | | {{dayjs(meeting.startTime).format("YYYY-MM-DD")}}<el-icon><Clock /></el-icon> |
| | | {{ formatTime(meeting.startTime) }} - {{ formatTime(meeting.endTime) }} |
| | | </div> |
| | | </div> |
| | |
| | | </div> |
| | | |
| | | <div class="meeting-agenda"> |
| | | <h4>è®®ç¨å®æ</h4> |
| | | <h4>ä¼è®®çºªè¦</h4> |
| | | <div class="agenda-list"> |
| | | <div class="editor-container"> |
| | | <div |
| | | v-for="(agenda, index) in meeting.agenda" |
| | | :key="index" |
| | | class="agenda-item" |
| | | :class="{ 'active': agenda.status === 'active', 'completed': agenda.status === 'completed' }" |
| | | > |
| | | <span class="agenda-time">{{ agenda.time }}</span> |
| | | <span class="agenda-content">{{ agenda.content }}</span> |
| | | <el-tag |
| | | :type="getAgendaStatusType(agenda.status)" |
| | | size="small" |
| | | > |
| | | {{ getAgendaStatusText(agenda.status) }} |
| | | </el-tag> |
| | | v-html="meeting.content" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- <div class="meeting-actions">--> |
| | | <!-- <el-button type="primary" size="small" @click="joinMeeting(meeting)">--> |
| | | <!-- å å
¥ä¼è®®--> |
| | | <!-- </el-button>--> |
| | | <!-- <el-button type="info" size="small" @click="viewDetails(meeting)">--> |
| | | <!-- æ¥ç详æ
--> |
| | | <!-- </el-button>--> |
| | | <!-- <el-button type="warning" size="small" @click="editMeeting(meeting)">--> |
| | | <!-- ç¼è¾--> |
| | | <!-- </el-button>--> |
| | | <!-- </div>--> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- å建ä¼è®®å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="dialogVisible" title="å建ä¼è®®" width="600px"> |
| | | <el-form :model="meetingForm" label-width="100px"> |
| | | <el-form-item label="ä¼è®®æ é¢"> |
| | | <el-input v-model="meetingForm.title" placeholder="请è¾å
¥ä¼è®®æ é¢" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼è®®æ¶é´"> |
| | | <el-date-picker |
| | | v-model="meetingForm.timeRange" |
| | | type="datetimerange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¶é´" |
| | | end-placeholder="ç»ææ¶é´" |
| | | format="YYYY-MM-DD HH:mm" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼è®®å°ç¹"> |
| | | <el-input v-model="meetingForm.location" placeholder="请è¾å
¥ä¼è®®å°ç¹" /> |
| | | </el-form-item> |
| | | <el-form-item label="主æäºº"> |
| | | <el-input v-model="meetingForm.host" placeholder="请è¾å
¥ä¸»æäººå§å" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼è®®æè¿°"> |
| | | <el-input |
| | | v-model="meetingForm.description" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥ä¼è®®æè¿°" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitMeeting">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { ref, reactive, onMounted } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { Clock, Location, User, UserFilled } from '@element-plus/icons-vue' |
| | | import Editor from "@/components/Editor/index.vue"; |
| | | import {getMeetSummaryItems,getMeetSummary} from '@/api/collaborativeApproval/meeting.js' |
| | | import dayjs from "dayjs"; |
| | | |
| | | // ç»è®¡æ°æ® |
| | | const stats = reactive({ |
| | | total: 12, |
| | | ongoing: 3, |
| | | completed: 7, |
| | | upcoming: 2 |
| | | const stats = ref({ |
| | | total: 0, |
| | | underWay: 0, |
| | | completed: 0, |
| | | toStart: 0 |
| | | }) |
| | | |
| | | // ä¼è®®æ°æ® |
| | | const meetings = ref([ |
| | | { |
| | | id: 1, |
| | | title: '产åå¼åå¨ä¼', |
| | | status: 'ongoing', |
| | | startTime: '2025-01-15 09:00:00', |
| | | endTime: '2025-01-15 10:30:00', |
| | | location: 'ä¼è®®å®¤A', |
| | | host: 'éå¿å¼º', |
| | | participants: ['éå¿å¼º', 'åé
å©·', 'ç建å½', '赵丽å'], |
| | | agenda: [ |
| | | { time: '09:00-09:15', content: 'ä¸å¨å·¥ä½æ»ç»', status: 'completed' }, |
| | | { time: '09:15-09:45', content: 'æ¬å¨å¼å计å', status: 'active' }, |
| | | { time: '09:45-10:00', content: 'ææ¯é¾ç¹è®¨è®º', status: 'pending' }, |
| | | { time: '10:00-10:30', content: 'é®é¢åé¦ä¸è§£å³', status: 'pending' } |
| | | ] |
| | | }, |
| | | { |
| | | id: 2, |
| | | title: '客æ·éæ±è¯å®¡ä¼', |
| | | status: 'upcoming', |
| | | startTime: '2025-01-15 14:00:00', |
| | | endTime: '2025-01-15 15:00:00', |
| | | location: '线ä¸ä¼è®®', |
| | | host: 'éå¿å¼º', |
| | | participants: ['éå¿å¼º', 'åé
å©·', 'åæå', '客æ·ä»£è¡¨'], |
| | | agenda: [ |
| | | { time: '14:00-14:20', content: 'éæ±èæ¯ä»ç»', status: 'pending' }, |
| | | { time: '14:20-14:40', content: 'åè½éæ±åæ', status: 'pending' }, |
| | | { time: '14:40-15:00', content: 'ææ¯å¯è¡æ§è¯ä¼°', status: 'pending' } |
| | | ] |
| | | }, |
| | | { |
| | | id: 3, |
| | | title: 'å¢é建设活å¨', |
| | | status: 'completed', |
| | | startTime: '2025-01-14 16:00:00', |
| | | endTime: '2025-01-14 18:00:00', |
| | | location: 'å
¬å¸å¤§å
', |
| | | host: '人äºé¨', |
| | | participants: ['å
¨ä½åå·¥'], |
| | | agenda: [ |
| | | { time: '16:00-16:30', content: 'å¢é游æ', status: 'completed' }, |
| | | { time: '16:30-17:00', content: 'ç»éªå享', status: 'completed' }, |
| | | { time: '17:00-18:00', content: 'èªç±äº¤æµ', status: 'completed' } |
| | | ] |
| | | } |
| | | |
| | | ]) |
| | | |
| | | // å¯¹è¯æ¡ç¸å
³ |
| | |
| | | // è·åç¶æç±»å |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | 'ongoing': 'success', |
| | | 'upcoming': 'warning', |
| | | 'completed': 'info' |
| | | '2': 'success', |
| | | '1': 'warning', |
| | | '0': 'info' |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | |
| | | // è·åç¶æææ¬ |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | 'ongoing': 'è¿è¡ä¸', |
| | | 'upcoming': 'å³å°å¼å§', |
| | | 'completed': '已宿' |
| | | '2': 'è¿è¡ä¸', |
| | | '1': 'å³å°å¼å§', |
| | | '0': '已宿' |
| | | } |
| | | return statusMap[status] || 'æªç¥' |
| | | } |
| | |
| | | return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }) |
| | | } |
| | | |
| | | // å建ä¼è®® |
| | | const createMeeting = () => { |
| | | dialogVisible.value = true |
| | | // é置表å |
| | | Object.assign(meetingForm, { |
| | | title: '', |
| | | timeRange: [], |
| | | location: '', |
| | | host: '', |
| | | description: '' |
| | | |
| | | onMounted( async () => { |
| | | let [resp1,resp2] = await Promise.all([getMeetSummary(),getMeetSummaryItems()]) |
| | | stats.value = resp1.data |
| | | meetings.value = resp2.data.map(item => { |
| | | return { |
| | | ...item, |
| | | participants: JSON.parse(item.participants) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // æäº¤ä¼è®® |
| | | const submitMeeting = () => { |
| | | if (!meetingForm.title || !meetingForm.timeRange.length || !meetingForm.location || !meetingForm.host) { |
| | | ElMessage.warning('请填å宿´çä¼è®®ä¿¡æ¯') |
| | | return |
| | | } |
| | | |
| | | // å建æ°ä¼è®® |
| | | const newMeeting = { |
| | | id: Date.now(), |
| | | title: meetingForm.title, |
| | | status: 'upcoming', |
| | | startTime: meetingForm.timeRange[0], |
| | | endTime: meetingForm.timeRange[1], |
| | | location: meetingForm.location, |
| | | host: meetingForm.host, |
| | | participants: [meetingForm.host], |
| | | agenda: [ |
| | | { time: 'å¾
å®', content: 'è®®ç¨å¾
å®', status: 'pending' } |
| | | ] |
| | | } |
| | | |
| | | meetings.value.unshift(newMeeting) |
| | | stats.total++ |
| | | stats.upcoming++ |
| | | |
| | | ElMessage.success('ä¼è®®å建æå') |
| | | dialogVisible.value = false |
| | | } |
| | | |
| | | // å å
¥ä¼è®® |
| | | const joinMeeting = (meeting) => { |
| | | ElMessage.success(`å·²å å
¥ä¼è®®ï¼${meeting.title}`) |
| | | } |
| | | |
| | | // æ¥ç详æ
|
| | | const viewDetails = (meeting) => { |
| | | ElMessage.info(`æ¥çä¼è®®è¯¦æ
ï¼${meeting.title}`) |
| | | } |
| | | |
| | | // ç¼è¾ä¼è®® |
| | | const editMeeting = (meeting) => { |
| | | ElMessage.info(`ç¼è¾ä¼è®®ï¼${meeting.title}`) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | console.log('ä¼è®®çæ¿é¡µé¢å è½½å®æ') |
| | | }) |
| | | </script> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>ä¼è®®è稿</h2> |
| | | <el-button type="primary" @click="handleAdd"> |
| | | <el-icon><Plus /></el-icon> |
| | | æ°å»ºè稿 |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- æç´¢åºå --> |
| | | <el-card class="search-card"> |
| | | <el-form :model="searchForm" label-width="100px" inline> |
| | | <el-form-item label="ä¼è®®ä¸»é¢"> |
| | | <el-input v-model="searchForm.title" placeholder="请è¾å
¥ä¼è®®ä¸»é¢" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼è®®æ¥æ"> |
| | | <el-date-picker |
| | | v-model="searchForm.meetingDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©ä¼è®®æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">æç´¢</el-button> |
| | | <el-button @click="resetSearch">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- è稿å表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="draftList" border> |
| | | <el-table-column prop="title" label="ä¼è®®ä¸»é¢" align="center" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column prop="room" 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="participants" label="åä¼äººæ°" align="center" width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="createTime" label="å建æ¶é´" align="center" width="180" /> |
| | | <el-table-column label="æä½" align="center" width="200" fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="viewDraft(scope.row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="editDraft(scope.row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="deleteDraft(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®è稿详æ
å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="ä¼è®®è稿详æ
" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | <div v-if="currentDraft"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="ä¼è®®ä¸»é¢">{{ currentDraft.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¼è®®ç¼å·">{{ currentDraft.meetingId }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¼è®®å®¤">{{ currentDraft.room }}</el-descriptions-item> |
| | | <el-descriptions-item label="主æäºº">{{ currentDraft.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¼è®®æ¶é´" :span="2"> |
| | | {{ formatDateTime(currentDraft.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å建æ¶é´">{{ currentDraft.createTime }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>åä¼äººå</h4> |
| | | <div class="participants-list"> |
| | | {{ currentDraft.participantList }} |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="content-section mt-20"> |
| | | <h4>ä¼è®®è¯´æ</h4> |
| | | <div class="meeting-description">{{ currentDraft.description }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">å
³ é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- æ°å»º/ç¼è¾èç¨¿å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | :title="dialogTitle" |
| | | v-model="editDialogVisible" |
| | | width="700px" |
| | | > |
| | | <el-form :model="meetingForm" :rules="rules" ref="meetingFormRef" label-width="100px"> |
| | | <el-form-item label="ä¼è®®ä¸»é¢" prop="title"> |
| | | <el-input v-model="meetingForm.title" placeholder="请è¾å
¥ä¼è®®ä¸»é¢" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼è®®å®¤" prop="room"> |
| | | <el-select v-model="meetingForm.roomId" placeholder="è¯·éæ©ä¼è®®å®¤" style="width: 100%"> |
| | | <el-option v-for="(v,k) in roomList" :label="v.name" :value="v.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="主æäºº" prop="host"> |
| | | <el-input v-model="meetingForm.host" placeholder="请è¾å
¥ä¸»æäºº" /> |
| | | </el-form-item> |
| | | <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-input |
| | | v-model="meetingForm.participants" |
| | | type="number" |
| | | placeholder="请è¾å
¥åä¼äººæ°" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="åä¼äººå" prop="participants"> |
| | | <el-input |
| | | v-model="meetingForm.participantList" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥åä¼äººåï¼ç¨éå·åé" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼è®®è¯´æ"> |
| | | <el-input |
| | | v-model="meetingForm.description" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥ä¼è®®è¯´æ" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="editDialogVisible = false">å æ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ä¿ å</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus } from '@element-plus/icons-vue' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import {getRoomEnum,getDraftList,saveDraft,delDraft} from '@/api/collaborativeApproval/meeting.js' |
| | | import dayjs from "dayjs"; |
| | | // æ°æ®å表å è½½ç¶æ |
| | | const loading = ref(false) |
| | | |
| | | // æ»æ¡æ° |
| | | const total = ref(0) |
| | | |
| | | // è稿åè¡¨æ°æ® |
| | | const draftList = ref([]) |
| | | |
| | | // æ¥è¯¢åæ° |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | meetingDate: '' |
| | | }) |
| | | |
| | | // æ¯å¦æ¾ç¤ºå¯¹è¯æ¡ |
| | | const detailDialogVisible = ref(false) |
| | | const editDialogVisible = ref(false) |
| | | |
| | | const roomList = ref([]) |
| | | |
| | | // å¯¹è¯æ¡æ é¢ |
| | | const dialogTitle = ref('') |
| | | |
| | | // å½åæ¥ççè稿 |
| | | const currentDraft = ref(null) |
| | | |
| | | // 表åå¼ç¨ |
| | | const meetingFormRef = ref(null) |
| | | |
| | | // æ¶é´é项ï¼ä»¥åå°æ¶ä¸ºé´éï¼å·¥ä½æ¶é´8:00-18:00ï¼ |
| | | const timeOptions = ref([]) |
| | | |
| | | // è¡¨åæ°æ® |
| | | const meetingForm = reactive({ |
| | | id: '', |
| | | meetingId: '', |
| | | title: '', |
| | | roomId: '', |
| | | host: '', |
| | | meetingDate: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | participants: 0, |
| | | participantList: '', |
| | | description: '', |
| | | createTime: '' |
| | | }) |
| | | |
| | | // è¡¨åæ ¡éªè§å |
| | | 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' }] |
| | | } |
| | | |
| | | // åå§åæ¶é´é项ï¼ä»¥åå°æ¶ä¸ºé´éï¼å·¥ä½æ¶é´8:00-18:00ï¼ |
| | | 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 getList = async () => { |
| | | loading.value = true |
| | | |
| | | let resp = await getDraftList({...queryParams,...searchForm}) |
| | | queryParams.current = resp.data.current |
| | | draftList.value = resp.data.records.map(it=>{ |
| | | it.room = roomList.value.find(room=>it.roomId===room.id).name ?? "" |
| | | it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format("HH:mm")} ~ ${dayjs(it.endTime).format("HH:mm")}` |
| | | return it |
| | | }) |
| | | |
| | | loading.value = false |
| | | |
| | | } |
| | | |
| | | // æç´¢æé®æä½ |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1 |
| | | getList() |
| | | } |
| | | |
| | | // éç½®æç´¢è¡¨å |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | createTime: [] |
| | | }) |
| | | handleSearch() |
| | | } |
| | | |
| | | // æ·»å æé®æä½ |
| | | const handleAdd = () => { |
| | | dialogTitle.value = 'æ°å»ºè稿' |
| | | resetForm() |
| | | editDialogVisible.value = true |
| | | } |
| | | |
| | | // æ¥çè稿详æ
|
| | | const viewDraft = (row) => { |
| | | currentDraft.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | |
| | | // ç¼è¾è稿 |
| | | const editDraft = (row) => { |
| | | dialogTitle.value = 'ç¼è¾è稿' |
| | | Object.assign(meetingForm, { |
| | | id: row.id, |
| | | meetingId: row.meetingId, |
| | | title: row.title, |
| | | room: row.room, |
| | | roomId: row.id, |
| | | host: row.host, |
| | | meetingDate: row.meetingTime.split(' ')[0], |
| | | startTime: row.meetingTime.split(' ')[1], |
| | | endTime: row.meetingTime.split(' ')[3], |
| | | participants: row.participants, |
| | | participantList: row.participantList, |
| | | description: row.description, |
| | | createTime: row.createTime |
| | | }) |
| | | editDialogVisible.value = true |
| | | } |
| | | |
| | | // å é¤è稿 |
| | | const deleteDraft = (row) => { |
| | | ElMessageBox.confirm( |
| | | `确认å é¤ä¼è®®è稿 "${row.title}"?`, |
| | | 'å é¤è稿', |
| | | { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | } |
| | | ).then(() => { |
| | | delDraft(row.id).then(resp=>{ |
| | | ElMessage.success('è稿å 餿å') |
| | | getList() |
| | | }) |
| | | |
| | | }).catch(() => {}) |
| | | } |
| | | |
| | | // é置表å |
| | | const resetForm = () => { |
| | | Object.assign(meetingForm, { |
| | | id: '', |
| | | meetingId: '', |
| | | title: '', |
| | | room: '', |
| | | host: '', |
| | | meetingDate: '', |
| | | startTime: '', |
| | | endTime: '', |
| | | participants: 0, |
| | | participantList: '', |
| | | description: '', |
| | | createTime: '' |
| | | }) |
| | | } |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | meetingFormRef.value.validate((valid) => { |
| | | if (valid) { |
| | | let formData = {...meetingForm} |
| | | formData.startTime = dayjs(meetingForm.meetingDate + ' ' + meetingForm.startTime).format("YYYY-MM-DD HH:mm:ss") |
| | | formData.endTime = dayjs(meetingForm.meetingDate + ' ' + meetingForm.endTime).format("YYYY-MM-DD HH:mm:ss") |
| | | saveDraft(formData).then(()=>{ |
| | | ElMessage.success('ä¿åæå') |
| | | editDialogVisible.value = false |
| | | getList() |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | |
| | | // 页é¢å è½½æ¶è·åæ°æ® |
| | | onMounted(() => { |
| | | initTimeOptions() |
| | | getList() |
| | | getRoomEnum().then((res) => { |
| | | roomList.value = res.data |
| | | }) |
| | | }) |
| | | </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; |
| | | } |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .meeting-description { |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | white-space: pre-wrap; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>ä¼è®®å®¡æ¹</h2> |
| | | </div> |
| | | |
| | | <!-- æç´¢åºå --> |
| | | <el-card class="search-card"> |
| | | <el-form :model="searchForm" inline> |
| | | <el-form-item label="ä¼è®®ä¸»é¢"> |
| | | <el-input v-model="searchForm.title" placeholder="请è¾å
¥ä¼è®®ä¸»é¢" clearable/> |
| | | </el-form-item> |
| | | <el-form-item label="ç³è¯·äºº"> |
| | | <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> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">æç´¢</el-button> |
| | | <el-button @click="resetSearch">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®å®¡æ¹å表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="approvalList" border> |
| | | <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"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <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"> |
| | | <template #default="scope"> |
| | | <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)" |
| | | > |
| | | å®¡æ¹ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®è¯¦æ
å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="ä¼è®®è¯¦æ
" |
| | | v-model="detailDialogVisible" |
| | | 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">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·äºº" label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <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"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¼è®®å°ç¹" label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <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-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·æ¶é´" label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <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" |
| | | :key="participant.id" |
| | | 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" |
| | | > |
| | | <div v-if="currentMeeting"> |
| | | <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"> |
| | | {{ 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" |
| | | :key="participant.id" |
| | | style="margin-right: 10px; margin-bottom: 10px;" |
| | | > |
| | | {{ participant.name }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-show="false" class="approval-opinion mt-20"> |
| | | <h4>å®¡æ¹æè§</h4> |
| | | <el-input |
| | | v-model="approvalOpinion" |
| | | type="textarea" |
| | | placeholder="请è¾å
¥å®¡æ¹æè§" |
| | | :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> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </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 {getStaffOnJob} from "@/api/personnelManagement/onboarding.js"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // æ°æ®å表å è½½ç¶æ |
| | | const loading = ref(false) |
| | | |
| | | // æ»æ¡æ° |
| | | const total = ref(0) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | // 审æ¹åè¡¨æ°æ® |
| | | const approvalList = ref([]) |
| | | |
| | | // æ¥è¯¢åæ° |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | |
| | | // æ¯å¦æ¾ç¤ºå¯¹è¯æ¡ |
| | | const detailDialogVisible = ref(false) |
| | | const approvalDialogVisible = ref(false) |
| | | |
| | | // å½åæ¥ççä¼è®® |
| | | const currentMeeting = ref(null) |
| | | |
| | | // å®¡æ¹æè§ |
| | | const approvalOpinion = ref('') |
| | | |
| | | // æ¥è¯¢æ°æ® |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getExamineList({...searchForm, ...queryParams}) |
| | | 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 => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}(${staff.postJob})` |
| | | } |
| | | }) |
| | | |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | |
| | | // æç´¢æé®æä½ |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1 |
| | | getList() |
| | | } |
| | | |
| | | // éç½®æç´¢è¡¨å |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | |
| | | // æ¥ç详æ
|
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | |
| | | // å¤çå®¡æ¹ |
| | | const handleApproval = (row) => { |
| | | currentMeeting.value = row |
| | | approvalOpinion.value = '' |
| | | approvalDialogVisible.value = true |
| | | } |
| | | |
| | | // è·åç¶æç±»å |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | '0': 'info', // å¾
å®¡æ¹ |
| | | '1': 'success', // å·²éè¿ |
| | | '2': 'warning', // æªéè¿ |
| | | '3': 'danger' // åæ¶ |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | |
| | | // è·åç¶æææ¬ |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | '0': 'å¾
审æ¹', |
| | | '1': 'å·²éè¿', |
| | | '2': 'æªéè¿', |
| | | '3': '已忶' |
| | | } |
| | | return statusMap[status] || 'æªç¥' |
| | | } |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | |
| | | // æäº¤å®¡æ¹ |
| | | const submitApproval = (status) => { |
| | | // if (status === 'approved' && !approvalOpinion.value.trim()) { |
| | | // ElMessage.warning('请填åå®¡æ¹æè§') |
| | | // return |
| | | // } |
| | | |
| | | ElMessageBox.confirm( |
| | | `确认${status === '1' ? 'éè¿' : 'ä¸éè¿'}该ä¼è®®ç³è¯·ï¼`, |
| | | '审æ¹ç¡®è®¤', |
| | | { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | } |
| | | ).then(() => { |
| | | saveMeetingApplication({ |
| | | id: currentMeeting.value.id, |
| | | status: status |
| | | }).then(resp=>{ |
| | | // æ´æ°ä¼è®®ç¶æ |
| | | currentMeeting.value.status = status |
| | | |
| | | ElMessage.success('å®¡æ¹æäº¤æå') |
| | | approvalDialogVisible.value = false |
| | | getList() |
| | | }) |
| | | |
| | | }).catch(() => { |
| | | }) |
| | | } |
| | | |
| | | // 页é¢å è½½æ¶è·åæ°æ® |
| | | onMounted(async () => { |
| | | const [resp1, resp2]= await Promise.all([getRoomEnum(), getStaffOnJob()]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data |
| | | |
| | | await getList() |
| | | }) |
| | | </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; |
| | | } |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .approval-opinion h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .nowrap-label { |
| | | white-space: nowrap !important; |
| | | } |
| | | |
| | | .description-content { |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | line-height: 1.6; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | min-height: 60px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>ä¼è®®å®¤ä½¿ç¨æ¥è¯¢</h2> |
| | | </div> |
| | | |
| | | <!-- æ¥è¯¢æ¡ä»¶ --> |
| | | <el-card class="search-card"> |
| | | <el-form :model="queryForm" label-width="80px" inline> |
| | | <el-form-item label="æ¥è¯¢æ¥æ"> |
| | | <el-date-picker |
| | | v-model="queryForm.meetingDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | :clearable="false" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">æ¥è¯¢</el-button> |
| | | <el-button @click="resetSearch">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®å®¤ä½¿ç¨æ
åµ --> |
| | | <el-card class="table-container" :loading="loading"> |
| | | <div class="time-table"> |
| | | <!-- 表头 --> |
| | | <div class="table-header"> |
| | | <div class="header-cell room-header">ä¼è®®å®¤</div> |
| | | <div |
| | | v-for="timeSlot in timeSlots" |
| | | :key="timeSlot.value" |
| | | class="header-cell time-header" |
| | | > |
| | | {{ timeSlot.label }} |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- è¡¨æ ¼å
容 --> |
| | | <div class="table-body"> |
| | | <div |
| | | v-for="room in roomUsage" |
| | | :key="room.id" |
| | | class="table-row" |
| | | > |
| | | <div class="cell room-cell">{{ room.name }}</div> |
| | | <div class="cells-container"> |
| | | <template v-for="(cell, index) in generateMeetingCells(room)" :key="index"> |
| | | <div |
| | | class="cell content-cell" |
| | | :class="[cell.type, `status-${cell.meeting?.status || '0'}`]" |
| | | :style="{ flex: cell.span-0.2 }" |
| | | @click="viewMeetingDetails(cell)" |
| | | > |
| | | <div v-if="cell.type === 'meeting'" class="meeting-content"> |
| | | <div class="meeting-title">{{ cell.meeting.title }}</div> |
| | | <div class="meeting-time">{{ cell.startTime }}-{{ cell.endTime }}</div> |
| | | </div> |
| | | <div v-else class="free-content"> |
| | | ç©ºé² |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®è¯¦æ
å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="ä¼è®®è¯¦æ
" |
| | | v-model="detailDialogVisible" |
| | | width="800px" |
| | | > |
| | | <div v-if="currentMeeting"> |
| | | <el-descriptions :column="1" border> |
| | | <el-descriptions-item label="ä¼è®®ä¸»é¢">{{ currentMeeting.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¼è®®å®¤">{{ currentMeeting.room }}</el-descriptions-item> |
| | | <el-descriptions-item label="ä¼è®®æ¶é´">{{ currentMeeting.time }}</el-descriptions-item> |
| | | <el-descriptions-item label="主æäºº">{{ currentMeeting.host }}</el-descriptions-item> |
| | | <el-descriptions-item label="åä¼äººæ°">{{ currentMeeting.participants }}人</el-descriptions-item> |
| | | <el-descriptions-item label="ä¼è®®è¯´æ">{{ currentMeeting.description }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="detailDialogVisible = false">å
³ é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref, reactive, onMounted} from 'vue' |
| | | import {ElMessage} from 'element-plus' |
| | | import {getMeetingUseList} from "@/api/collaborativeApproval/meeting.js" |
| | | import dayjs from "dayjs"; |
| | | |
| | | // æ¥è¯¢è¡¨å |
| | | const queryForm = reactive({ |
| | | meetingDate: dayjs().format('YYYY-MM-DD') |
| | | }) |
| | | let loading = ref(false) |
| | | // æ¶é´æ®µï¼ä»¥åå°æ¶ä¸ºé´éï¼ |
| | | const timeSlots = ref([]) |
| | | |
| | | // ä¼è®®å®¤ä½¿ç¨æ
åµ |
| | | const roomUsage = ref([]) |
| | | |
| | | // å½åæ¥ççä¼è®® |
| | | const currentMeeting = ref(null) |
| | | |
| | | // æ¯å¦æ¾ç¤ºè¯¦æ
å¯¹è¯æ¡ |
| | | const detailDialogVisible = ref(false) |
| | | |
| | | // åå§åæ¶é´æ§½ï¼ä»¥åå°æ¶ä¸ºé´éï¼ä»8:00å°18:00ï¼ |
| | | const initTimeSlots = () => { |
| | | const slots = [] |
| | | for (let hour = 8; hour < 18; hour++) { |
| | | // æ¯ä¸ªå°æ¶æ·»å 两个æ¶é´æ®µï¼æ´ç¹ååç¹ |
| | | slots.push({ |
| | | label: `${hour.toString().padStart(2, '0')}:00`, |
| | | value: `${hour.toString().padStart(2, '0')}:00` |
| | | }) |
| | | |
| | | if (hour < 18) { // å°17:30ä¸ºæ¢ |
| | | slots.push({ |
| | | label: `${hour.toString().padStart(2, '0')}:30`, |
| | | value: `${hour.toString().padStart(2, '0')}:30` |
| | | }) |
| | | } |
| | | } |
| | | timeSlots.value = slots |
| | | } |
| | | |
| | | // çæä¼è®®å®¤çæ¶é´åå
æ ¼ |
| | | const generateMeetingCells = (room) => { |
| | | const cells = [] |
| | | const meetings = room.meetings || [] |
| | | const occupiedSlots = new Set() |
| | | |
| | | // å¤çæ¯ä¸ªä¼è®® |
| | | for (const meeting of meetings) { |
| | | |
| | | const startIdx = timeSlots.value.findIndex(slot => slot.value === meeting.startTime) |
| | | let endIdx = timeSlots.value.findIndex(slot => slot.value === meeting.endTime) |
| | | if (endIdx === -1) { |
| | | endIdx = timeSlots.value.length |
| | | } |
| | | console.log('endIdx111', endIdx) |
| | | if (startIdx !== -1 && endIdx !== -1) { |
| | | // æ 记被å ç¨çæ¶é´æ®µ |
| | | for (let i = startIdx; i < endIdx; i++) { |
| | | occupiedSlots.add(timeSlots.value[i].value) |
| | | } |
| | | |
| | | // å建ä¼è®®åå
æ ¼ |
| | | cells.push({ |
| | | type: 'meeting', |
| | | meeting: meeting, |
| | | span: endIdx - startIdx, |
| | | startTime: meeting.startTime, |
| | | endTime: meeting.endTime |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // å¤çç©ºé²æ¶é´æ®µ |
| | | for (let i = 0; i < timeSlots.value.length; i++) { |
| | | const slot = timeSlots.value[i] |
| | | if (!occupiedSlots.has(slot.value)) { |
| | | // æ¥æ¾è¿ç»çç©ºé²æ¶é´æ®µ |
| | | let span = 1 |
| | | while (i + span < timeSlots.value.length && |
| | | !occupiedSlots.has(timeSlots.value[i + span].value)) { |
| | | occupiedSlots.add(timeSlots.value[i + span].value) |
| | | span++ |
| | | } |
| | | |
| | | cells.push({ |
| | | type: 'free', |
| | | span: span, |
| | | time: slot.value |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // ææ¶é´æåº |
| | | cells.sort((a, b) => { |
| | | const timeA = a.startTime || a.time |
| | | const timeB = b.startTime || b.time |
| | | return timeSlots.value.findIndex(s => s.value === timeA) - |
| | | timeSlots.value.findIndex(s => s.value === timeB) |
| | | }) |
| | | console.log('cells', cells) |
| | | return cells |
| | | } |
| | | |
| | | // æ¥çä¼è®®è¯¦æ
|
| | | const viewMeetingDetails = (cell) => { |
| | | if (cell && cell.type === 'meeting') { |
| | | currentMeeting.value = cell.meeting |
| | | detailDialogVisible.value = true |
| | | } else { |
| | | ElMessage.info('该æ¶é´æ®µä¼è®®å®¤ç©ºé²') |
| | | } |
| | | } |
| | | |
| | | // æ¥è¯¢æé®æä½ |
| | | const handleSearch = async () => { |
| | | loading.value = true |
| | | let resp = await getMeetingUseList({...queryForm}) |
| | | roomUsage.value = resp.data |
| | | loading.value = false |
| | | } |
| | | |
| | | // éç½®æç´¢è¡¨å |
| | | const resetSearch = () => { |
| | | queryForm.date = dayjs().format('YYYY-MM-DD') |
| | | } |
| | | |
| | | // 页é¢å è½½æ¶è·åæ°æ® |
| | | onMounted(() => { |
| | | // åå§åæ¶é´æ§½ |
| | | initTimeSlots() |
| | | |
| | | // é»è®¤æ¥è¯¢ä»å¤©çæ°æ® |
| | | const today = new Date() |
| | | queryForm.date = today.toISOString().split('T')[0] |
| | | handleSearch() |
| | | }) |
| | | </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; |
| | | } |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .table-container { |
| | | padding: 0; |
| | | } |
| | | |
| | | .time-table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | } |
| | | |
| | | .table-header { |
| | | display: flex; |
| | | border: 1px solid; |
| | | } |
| | | |
| | | .table-row { |
| | | display: flex; |
| | | border: 1px solid #ebeef5; |
| | | border-top: none; |
| | | } |
| | | |
| | | .header-cell { |
| | | padding: 12px 5px; |
| | | text-align: center; |
| | | font-weight: bold; |
| | | border-right: 1px solid; |
| | | min-height: 20px; |
| | | } |
| | | |
| | | .room-header { |
| | | width: 120px; |
| | | } |
| | | |
| | | .time-header { |
| | | flex: 1; |
| | | } |
| | | |
| | | .cell { |
| | | padding: 15px 5px; |
| | | text-align: center; |
| | | border-right: 1px solid; |
| | | min-height: 20px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | word-break: break-word; |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | .room-cell { |
| | | width: 120px; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .cells-container { |
| | | flex: 1; |
| | | display: flex; |
| | | } |
| | | |
| | | .content-cell { |
| | | min-height: 60px; |
| | | cursor: pointer; |
| | | transition: all 0.3s; |
| | | } |
| | | |
| | | .content-cell:hover { |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .free { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .meeting { |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .status-1 { |
| | | background-color: #fef0f0; |
| | | color: #d14646; |
| | | } |
| | | |
| | | .status-0 { |
| | | background-color: #c7ddc8; |
| | | color: rgba(230, 162, 60, 0.29); |
| | | } |
| | | |
| | | .meeting-content { |
| | | width: 100%; |
| | | } |
| | | |
| | | .meeting-title { |
| | | font-weight: bold; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .meeting-time { |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .free-content { |
| | | color: #909399; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>ä¼è®®åå¸</h2> |
| | | </div> |
| | | |
| | | <!-- æç´¢åºå --> |
| | | <el-card class="search-card"> |
| | | <el-form :model="searchForm" inline> |
| | | <el-form-item label="ä¼è®®ä¸»é¢"> |
| | | <el-input v-model="searchForm.title" placeholder="请è¾å
¥ä¼è®®ä¸»é¢" clearable/> |
| | | </el-form-item> |
| | | <el-form-item label="ç³è¯·äºº"> |
| | | <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> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">æç´¢</el-button> |
| | | <el-button @click="resetSearch">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®åå¸å表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="approvalList" border> |
| | | <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"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <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"> |
| | | <template #default="scope"> |
| | | <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)" |
| | | > |
| | | åå¸ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®è¯¦æ
å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="ä¼è®®è¯¦æ
" |
| | | v-model="detailDialogVisible" |
| | | 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">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·äºº" label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <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"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¼è®®å°ç¹" label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <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-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·æ¶é´" label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <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" |
| | | :key="participant.id" |
| | | 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" |
| | | > |
| | | <div v-if="currentMeeting"> |
| | | <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"> |
| | | {{ 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" |
| | | :key="participant.id" |
| | | 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" |
| | | type="textarea" |
| | | placeholder="请è¾å
¥å叿è§" |
| | | :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> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </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 {getStaffOnJob} from "@/api/personnelManagement/onboarding.js"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // æ°æ®å表å è½½ç¶æ |
| | | const loading = ref(false) |
| | | |
| | | // æ»æ¡æ° |
| | | const total = ref(0) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | // åå¸åè¡¨æ°æ® |
| | | const approvalList = ref([]) |
| | | |
| | | // æ¥è¯¢åæ° |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | |
| | | // æ¯å¦æ¾ç¤ºå¯¹è¯æ¡ |
| | | const detailDialogVisible = ref(false) |
| | | const approvalDialogVisible = ref(false) |
| | | |
| | | // å½åæ¥ççä¼è®® |
| | | const currentMeeting = ref(null) |
| | | |
| | | // åå¸æè§ |
| | | const publishComment = ref('') |
| | | |
| | | // æ¥è¯¢æ°æ® |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getMeetingPublish({...searchForm, ...queryParams}) |
| | | 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 => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}(${staff.postJob})` |
| | | } |
| | | }) |
| | | |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | |
| | | // æç´¢æé®æä½ |
| | | const handleSearch = () => { |
| | | queryParams.pageNum = 1 |
| | | getList() |
| | | } |
| | | |
| | | // éç½®æç´¢è¡¨å |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | status: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | |
| | | // æ¥ç详æ
|
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | |
| | | // å¤çåå¸ |
| | | const handleApproval = (row) => { |
| | | currentMeeting.value = row |
| | | publishComment.value = '' |
| | | approvalDialogVisible.value = true |
| | | } |
| | | |
| | | // è·åç¶æç±»å |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | '0': 'info', // å¾
åå¸ |
| | | '1': 'success', // å·²éè¿ |
| | | '2': 'danger', // æªéè¿ |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | |
| | | // è·åç¶æææ¬ |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | '0': 'å¾
åå¸', |
| | | '1': 'å·²åå¸', |
| | | '2': '已忶', |
| | | } |
| | | return statusMap[status] || 'æªç¥' |
| | | } |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | |
| | | // æäº¤åå¸ |
| | | const submitApproval = (status) => { |
| | | // if (status === 'approved' && !publishComment.value.trim()) { |
| | | // ElMessage.warning('请填åå叿è§') |
| | | // return |
| | | // } |
| | | |
| | | ElMessageBox.confirm( |
| | | `确认${status === '1' ? 'åå¸' : 'åæ¶'}该ä¼è®®ï¼`, |
| | | 'åå¸ç¡®è®¤', |
| | | { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | } |
| | | ).then(() => { |
| | | saveMeetingApplication({ |
| | | id: currentMeeting.value.id, |
| | | publishStatus: status, |
| | | publishComment: publishComment.value |
| | | }).then(resp=>{ |
| | | // æ´æ°ä¼è®®ç¶æ |
| | | currentMeeting.value.status = status |
| | | |
| | | ElMessage.success('åå¸æäº¤æå') |
| | | approvalDialogVisible.value = false |
| | | getList() |
| | | }) |
| | | |
| | | }).catch(() => { |
| | | }) |
| | | } |
| | | |
| | | // 页é¢å è½½æ¶è·åæ°æ® |
| | | onMounted(async () => { |
| | | const [resp1, resp2]= await Promise.all([getRoomEnum(), getStaffOnJob()]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data |
| | | |
| | | await getList() |
| | | }) |
| | | </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; |
| | | } |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .approval-opinion h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .nowrap-label { |
| | | white-space: nowrap !important; |
| | | } |
| | | |
| | | .description-content { |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | line-height: 1.6; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | min-height: 60px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>ä¼è®®å®¤è®¾ç½®</h2> |
| | | <el-button type="primary" @click="handleAdd"> |
| | | <el-icon><Plus /></el-icon> |
| | | æ°å¢ä¼è®®å®¤ |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- æç´¢åºå --> |
| | | <el-card class="search-card"> |
| | | <el-form :model="searchForm" label-width="100px" inline> |
| | | <el-form-item label="ä¼è®®å®¤åç§°"> |
| | | <el-input v-model="searchForm.name" placeholder="请è¾å
¥ä¼è®®å®¤åç§°" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="ä½ç½®"> |
| | | <el-input v-model="searchForm.location" placeholder="请è¾å
¥ä½ç½®" clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">æç´¢</el-button> |
| | | <el-button @click="resetSearch">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®å®¤å表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="meetingRoomList" border> |
| | | <el-table-column prop="name" label="ä¼è®®å®¤åç§°" align="center" /> |
| | | <el-table-column prop="location" label="ä½ç½®" align="center" /> |
| | | <el-table-column prop="capacity" label="容纳人æ°" align="center" /> |
| | | <el-table-column prop="equipment" label="设å¤é
ç½®" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-for="item in scope.row.equipment" :key="item" style="margin-right: 5px; margin-bottom: 5px;"> |
| | | {{ item }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="ç¶æ" align="center" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'"> |
| | | {{ scope.row.status === 1 ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" align="center" width="200"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="handleEdit(scope.row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | </el-card> |
| | | |
| | | <!-- æ·»å /ç¼è¾å¯¹è¯æ¡ --> |
| | | <el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" @close="cancel"> |
| | | <el-form ref="meetingRoomFormRef" :model="meetingRoomForm" :rules="rules" label-width="100px"> |
| | | <el-form-item label="ä¼è®®å®¤åç§°" prop="name"> |
| | | <el-input v-model="meetingRoomForm.name" placeholder="请è¾å
¥ä¼è®®å®¤åç§°" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä½ç½®" prop="location"> |
| | | <el-input v-model="meetingRoomForm.location" placeholder="请è¾å
¥ä¼è®®å®¤ä½ç½®" /> |
| | | </el-form-item> |
| | | <el-form-item label="容纳人æ°" prop="capacity"> |
| | | <el-input-number v-model="meetingRoomForm.capacity" :min="1" placeholder="请è¾å
¥å®¹çº³äººæ°" /> |
| | | </el-form-item> |
| | | <el-form-item label="设å¤é
ç½®" prop="equipment"> |
| | | <el-select v-model="meetingRoomForm.equipment" multiple placeholder="è¯·éæ©è®¾å¤é
ç½®" style="width: 100%"> |
| | | <el-option |
| | | v-for="item in equipmentOptions" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-radio-group v-model="meetingRoomForm.status"> |
| | | <el-radio :label="1">å¯ç¨</el-radio> |
| | | <el-radio :label="0">ç¦ç¨</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="meetingRoomForm.remark" type="textarea" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="cancel">å æ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">ç¡® å®</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus } from '@element-plus/icons-vue' |
| | | import Pagination from '@/components/Pagination/index.vue' |
| | | import {getMeetingRoomList,saveRoom,delRoom} from '@/api/collaborativeApproval/meeting.js' |
| | | |
| | | // æ°æ®å表å è½½ç¶æ |
| | | const loading = ref(false) |
| | | |
| | | // æ»æ¡æ° |
| | | const total = ref(0) |
| | | |
| | | // ä¼è®®å®¤åè¡¨æ°æ® |
| | | const meetingRoomList = ref([]) |
| | | |
| | | // æ¥è¯¢åæ° |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = reactive({ |
| | | name: '', |
| | | location: '' |
| | | }) |
| | | |
| | | // å¯¹è¯æ¡æ é¢ |
| | | const dialogTitle = ref('') |
| | | |
| | | // æ¯å¦æ¾ç¤ºå¯¹è¯æ¡ |
| | | const dialogVisible = ref(false) |
| | | |
| | | // 设å¤é
ç½®é项 |
| | | const equipmentOptions = ref([ |
| | | { value: 'æå½±ä»ª', label: 'æå½±ä»ª' }, |
| | | { value: 'çµè§', label: 'çµè§' }, |
| | | { value: 'é³å', label: 'é³å' }, |
| | | { value: 'çµè¯', label: 'çµè¯' }, |
| | | { value: 'è§é¢ä¼è®®ç³»ç»', label: 'è§é¢ä¼è®®ç³»ç»' }, |
| | | { value: 'ç½æ¿', label: 'ç½æ¿' }, |
| | | { value: 'ååæ¿', label: 'ååæ¿' }, |
| | | { value: 'æ 线ç½ç»', label: 'æ 线ç½ç»' } |
| | | ]) |
| | | |
| | | // è¡¨åæ°æ® |
| | | const meetingRoomForm = reactive({ |
| | | id: undefined, |
| | | name: '', |
| | | location: '', |
| | | capacity: 10, |
| | | equipment: [], |
| | | status: 1, |
| | | remark: '' |
| | | }) |
| | | |
| | | // è¡¨åæ ¡éªè§å |
| | | const rules = { |
| | | name: [{ required: true, message: 'ä¼è®®å®¤åç§°ä¸è½ä¸ºç©º', trigger: 'blur' }], |
| | | location: [{ required: true, message: 'ä½ç½®ä¸è½ä¸ºç©º', trigger: 'blur' }], |
| | | capacity: [{ required: true, message: '容纳人æ°ä¸è½ä¸ºç©º', trigger: 'blur' }] |
| | | } |
| | | |
| | | // 表åå¼ç¨ |
| | | const meetingRoomFormRef = ref(null) |
| | | |
| | | // æ¥è¯¢æ°æ® |
| | | const getList = async () => { |
| | | loading.value = true |
| | | |
| | | let resp = await getMeetingRoomList({...searchForm,...queryParams}) |
| | | meetingRoomList.value = resp.data.records.map(it=>{ |
| | | it.equipment = it.equipment.split(',') |
| | | return it; |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | |
| | | } |
| | | |
| | | // æç´¢æé®æä½ |
| | | const handleSearch = () => { |
| | | queryParams.current = 1 |
| | | getList() |
| | | } |
| | | |
| | | // éç½®æç´¢è¡¨å |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | name: '', |
| | | location: '' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | |
| | | // æ·»å æé®æä½ |
| | | const handleAdd = () => { |
| | | dialogTitle.value = 'æ·»å ä¼è®®å®¤' |
| | | dialogVisible.value = true |
| | | } |
| | | |
| | | // ä¿®æ¹æé®æä½ |
| | | const handleEdit = (row) => { |
| | | dialogTitle.value = 'ä¿®æ¹ä¼è®®å®¤' |
| | | Object.assign(meetingRoomForm, row) |
| | | dialogVisible.value = true |
| | | } |
| | | |
| | | // å é¤æé®æä½ |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm( |
| | | `æ¯å¦ç¡®è®¤å é¤ä¼è®®å®¤ "${row.name}"?`, |
| | | 'è¦å', |
| | | { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | } |
| | | ).then(() => { |
| | | // 模æå é¤æä½ |
| | | delRoom(row.id).then(resp=>{ |
| | | ElMessage.success('å 餿å') |
| | | getList() |
| | | }) |
| | | |
| | | }).catch(() => {}) |
| | | } |
| | | |
| | | // åæ¶æé® |
| | | const cancel = () => { |
| | | dialogVisible.value = false |
| | | reset() |
| | | } |
| | | |
| | | // 表åéç½® |
| | | const reset = () => { |
| | | Object.assign(meetingRoomForm, { |
| | | id: undefined, |
| | | name: '', |
| | | location: '', |
| | | capacity: 10, |
| | | equipment: [], |
| | | status: 1, |
| | | remark: '' |
| | | }) |
| | | meetingRoomFormRef.value?.resetFields() |
| | | } |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | meetingRoomFormRef.value?.validate((valid) => { |
| | | if (valid) { |
| | | // 模ææäº¤æä½ |
| | | |
| | | let formData = {... meetingRoomForm} |
| | | formData.equipment = formData.equipment.join(',') |
| | | saveRoom(formData).then(resp=>{ |
| | | ElMessage.success('ä¿åæå') |
| | | dialogVisible.value = false |
| | | getList() |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 页é¢å è½½æ¶è·åæ°æ® |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | </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; |
| | | } |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>ä¼è®®çºªè¦</h2> |
| | | </div> |
| | | |
| | | <!-- æç´¢åºå --> |
| | | <el-card class="search-card"> |
| | | <el-form :model="searchForm" inline> |
| | | <el-form-item label="ä¼è®®ä¸»é¢"> |
| | | <el-input v-model="searchForm.title" placeholder="请è¾å
¥ä¼è®®ä¸»é¢" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="ç³è¯·äºº"> |
| | | <el-input v-model="searchForm.applicant" placeholder="请è¾å
¥ç³è¯·äºº" clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">æç´¢</el-button> |
| | | <el-button @click="resetSearch">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®å表 --> |
| | | <el-card> |
| | | <el-table v-loading="loading" :data="meetingList" border> |
| | | <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"> |
| | | <template #default="scope"> |
| | | {{ scope.row.participants.length }}人 |
| | | </template> |
| | | </el-table-column> |
| | | <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" |
| | | link |
| | | @click="addMinutes(scope.row)" |
| | | > |
| | | æ·»å çºªè¦ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="getList" |
| | | /> |
| | | </el-card> |
| | | |
| | | <!-- ä¼è®®è¯¦æ
å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="ä¼è®®è¯¦æ
" |
| | | v-model="detailDialogVisible" |
| | | 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">{{ |
| | | currentMeeting.title |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·äºº" label-class-name="nowrap-label">{{ |
| | | currentMeeting.applicant |
| | | }}</el-descriptions-item> |
| | | <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"> |
| | | {{ formatDateTime(currentMeeting.meetingTime) }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¼è®®å°ç¹" label-class-name="nowrap-label">{{ |
| | | currentMeeting.location |
| | | }}</el-descriptions-item> |
| | | <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-tag :type="getStatusType(currentMeeting.status)"> |
| | | {{ getStatusText(currentMeeting.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç³è¯·æ¶é´" label-class-name="nowrap-label">{{ |
| | | currentMeeting.createTime |
| | | }}</el-descriptions-item> |
| | | <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" |
| | | :key="participant.id" |
| | | 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="minutesDialogVisible" |
| | | width="80%" |
| | | @close="handleCloseMinutesDialog" |
| | | > |
| | | <div v-if="currentMeeting"> |
| | | <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"> |
| | | {{ 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" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="minutesDialogVisible = false">å æ¶</el-button> |
| | | <el-button type="primary" @click="submitMinutes">ä¿ å</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </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 { getStaffOnJob } from "@/api/personnelManagement/onboarding.js" |
| | | import dayjs from "dayjs" |
| | | |
| | | // æ°æ®å表å è½½ç¶æ |
| | | const loading = ref(false) |
| | | |
| | | // æ»æ¡æ° |
| | | const total = ref(0) |
| | | const roomEnum = ref([]) |
| | | const staffList = ref([]) |
| | | |
| | | // ä¼è®®åè¡¨æ°æ® |
| | | const meetingList = ref([]) |
| | | |
| | | // æ¥è¯¢åæ° |
| | | const queryParams = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }) |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = reactive({ |
| | | title: '', |
| | | applicant: '', |
| | | // status: '1' // é»è®¤åªæ¾ç¤ºå·²éè¿å®¡æ¹çä¼è®® |
| | | }) |
| | | |
| | | // æ¯å¦æ¾ç¤ºå¯¹è¯æ¡ |
| | | const detailDialogVisible = ref(false) |
| | | const minutesDialogVisible = ref(false) |
| | | |
| | | // å½åæ¥ççä¼è®® |
| | | const currentMeeting = ref(null) |
| | | |
| | | // ä¼è®®çºªè¦å
容 |
| | | const minutesContent = ref('') |
| | | const minutesContentId = ref('') |
| | | |
| | | // æ¥è¯¢æ°æ® |
| | | const getList = async () => { |
| | | loading.value = true |
| | | let resp = await getMeetingPublish({ ...searchForm, ...queryParams }) |
| | | 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 => { |
| | | return { |
| | | id: staff.id, |
| | | name: `${staff.staffName}(${staff.postJob})` |
| | | } |
| | | }) |
| | | |
| | | return it |
| | | }) |
| | | total.value = resp.data.total |
| | | loading.value = false |
| | | } |
| | | |
| | | // æç´¢æé®æä½ |
| | | const handleSearch = () => { |
| | | queryParams.current = 1 |
| | | getList() |
| | | } |
| | | |
| | | // éç½®æç´¢è¡¨å |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { |
| | | title: '', |
| | | applicant: '', |
| | | // status: '1' |
| | | }) |
| | | handleSearch() |
| | | } |
| | | |
| | | // æ¥ç详æ
|
| | | const viewDetail = (row) => { |
| | | currentMeeting.value = row |
| | | detailDialogVisible.value = true |
| | | } |
| | | |
| | | // æ·»å ä¼è®®çºªè¦ |
| | | 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 |
| | | }else { |
| | | minutesContent.value = `<h2>${row.title}ä¼è®®çºªè¦</h2> |
| | | <p><strong>ä¼è®®æ¶é´ï¼</strong>${row.meetingTime}</p> |
| | | <p><strong>ä¼è®®å°ç¹ï¼</strong>${row.location}</p> |
| | | <p><strong>主æäººï¼</strong>${row.host}</p> |
| | | <p><strong>åä¼äººåï¼</strong></p> |
| | | <ol> |
| | | ${row.participants.map(p => `<li>${p.name}</li>`).join('')} |
| | | </ol> |
| | | <p><strong>ä¼è®®å
容ï¼</strong></p> |
| | | <ol> |
| | | <li>è®®é¢ä¸ï¼ |
| | | <ul> |
| | | <li>讨论å
容ï¼</li> |
| | | <li>å³è®®äºé¡¹ï¼</li> |
| | | </ul> |
| | | </li> |
| | | <li>è®®é¢äºï¼ |
| | | <ul> |
| | | <li>讨论å
容ï¼</li> |
| | | <li>å³è®®äºé¡¹ï¼</li> |
| | | </ul> |
| | | </li> |
| | | </ol> |
| | | <p><strong>夿³¨ï¼</strong></p>` |
| | | } |
| | | |
| | | minutesDialogVisible.value = true |
| | | } |
| | | |
| | | // æäº¤ä¼è®®çºªè¦ |
| | | const submitMinutes = () => { |
| | | if (!minutesContent.value) { |
| | | ElMessage.warning('请è¾å
¥ä¼è®®çºªè¦å
容') |
| | | return |
| | | } |
| | | saveMeetingMinutes({ |
| | | id: minutesContentId.value, |
| | | content: minutesContent.value, |
| | | meetingId: currentMeeting.value.id, |
| | | title: currentMeeting.value.title |
| | | }).then(resp=>{ |
| | | console.log('ä¼è®®çºªè¦å
容:', minutesContent.value) |
| | | ElMessage.success('ä¼è®®çºªè¦ä¿åæå') |
| | | minutesDialogVisible.value = false |
| | | }) |
| | | |
| | | } |
| | | |
| | | // å
³éä¼è®®çºªè¦å¯¹è¯æ¡ |
| | | const handleCloseMinutesDialog = () => { |
| | | minutesContent.value = '' |
| | | } |
| | | |
| | | // è·åç¶æç±»å |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | '0': 'info', // å¾
å®¡æ¹ |
| | | '1': 'success', // å·²éè¿ |
| | | '2': 'warning', // æªéè¿ |
| | | '3': 'danger' // åæ¶ |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | |
| | | // è·åç¶æææ¬ |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | '0': 'å¾
审æ¹', |
| | | '1': 'å·²éè¿', |
| | | '2': 'æªéè¿', |
| | | '3': '已忶' |
| | | } |
| | | return statusMap[status] || 'æªç¥' |
| | | } |
| | | |
| | | // æ ¼å¼åæ¥ææ¶é´ |
| | | const formatDateTime = (dateTime) => { |
| | | if (!dateTime) return '' |
| | | return dateTime.replace(' ', '\n') |
| | | } |
| | | |
| | | // 页é¢å è½½æ¶è·åæ°æ® |
| | | onMounted(async () => { |
| | | const [resp1, resp2] = await Promise.all([getRoomEnum(), getStaffOnJob()]) |
| | | roomEnum.value = resp1.data |
| | | staffList.value = resp2.data |
| | | |
| | | await getList() |
| | | }) |
| | | </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; |
| | | } |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .content-section h4 { |
| | | margin: 0 0 15px 0; |
| | | color: #303133; |
| | | } |
| | | |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .participants-list { |
| | | min-height: 40px; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .nowrap-label { |
| | | white-space: nowrap !important; |
| | | } |
| | | |
| | | .editor-container { |
| | | border: 1px solid #dcdfe6; |
| | | border-radius: 4px; |
| | | } |
| | | </style> |