From 6f1bd54c8ea5327f09e077af3d16272987c48e40 Mon Sep 17 00:00:00 2001
From: buhuazhen <hua100783@gmail.com>
Date: 星期三, 17 九月 2025 16:22:17 +0800
Subject: [PATCH] Merge branch 'dev_meet' into dev
---
src/views/collaborativeApproval/notificationManagement/summary/index.vue | 403 +++++++
src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue | 418 +++++++
src/views/collaborativeApproval/meetingBoard/index.vue | 228 ---
src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue | 416 +++++++
src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue | 306 +++++
src/api/collaborativeApproval/meeting.js | 118 ++
src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue | 398 +++++++
src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue | 495 +++++++++
src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue | 371 +++++++
9 files changed, 2,963 insertions(+), 190 deletions(-)
diff --git a/src/api/collaborativeApproval/meeting.js b/src/api/collaborativeApproval/meeting.js
new file mode 100644
index 0000000..20df8b1
--- /dev/null
+++ b/src/api/collaborativeApproval/meeting.js
@@ -0,0 +1,118 @@
+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",
+ });
+}
diff --git a/src/views/collaborativeApproval/meetingBoard/index.vue b/src/views/collaborativeApproval/meetingBoard/index.vue
index b6b803b..dfbb922 100644
--- a/src/views/collaborativeApproval/meetingBoard/index.vue
+++ b/src/views/collaborativeApproval/meetingBoard/index.vue
@@ -16,7 +16,7 @@
</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>
@@ -28,7 +28,7 @@
</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>
@@ -45,11 +45,11 @@
</el-tag>
</div>
<div class="meeting-time">
- <el-icon><Clock /></el-icon>
- {{ formatTime(meeting.startTime) }} - {{ formatTime(meeting.endTime) }}
+ {{dayjs(meeting.startTime).format("YYYY-MM-DD")}}<el-icon><Clock /></el-icon>
+ {{ formatTime(meeting.startTime) }} - {{ formatTime(meeting.endTime) }}
</div>
</div>
-
+
<div class="meeting-info">
<div class="info-item">
<el-icon><Location /></el-icon>
@@ -66,79 +66,18 @@
</div>
<div class="meeting-agenda">
- <h4>璁▼瀹夋帓</h4>
+ <h4>浼氳绾</h4>
<div class="agenda-list">
- <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>
+ <div class="editor-container">
+ <div
+ 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>
@@ -146,63 +85,21 @@
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: '浼氳瀹',
- 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: ['闄堝織寮�', '鍒橀泤濠�', '瀛欐槑鍗�', '瀹㈡埛浠h〃'],
- 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' }
- ]
- }
+
])
// 瀵硅瘽妗嗙浉鍏�
@@ -218,9 +115,9 @@
// 鑾峰彇鐘舵�佺被鍨�
const getStatusType = (status) => {
const statusMap = {
- 'ongoing': 'success',
- 'upcoming': 'warning',
- 'completed': 'info'
+ '2': 'success',
+ '1': 'warning',
+ '0': 'info'
}
return statusMap[status] || 'info'
}
@@ -228,9 +125,9 @@
// 鑾峰彇鐘舵�佹枃鏈�
const getStatusText = (status) => {
const statusMap = {
- 'ongoing': '杩涜涓�',
- 'upcoming': '鍗冲皢寮�濮�',
- 'completed': '宸插畬鎴�'
+ '2': '杩涜涓�',
+ '1': '鍗冲皢寮�濮�',
+ '0': '宸插畬鎴�'
}
return statusMap[status] || '鏈煡'
}
@@ -261,65 +158,16 @@
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>
@@ -480,19 +328,19 @@
.stats-cards {
grid-template-columns: repeat(2, 1fr);
}
-
+
.meeting-header {
flex-direction: column;
gap: 10px;
}
-
+
.meeting-info {
flex-direction: column;
gap: 10px;
}
-
+
.meeting-actions {
flex-direction: column;
}
}
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue b/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
new file mode 100644
index 0000000..1a2859a
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
@@ -0,0 +1,398 @@
+<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>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue b/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue
new file mode 100644
index 0000000..0c62b8b
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue
@@ -0,0 +1,495 @@
+<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>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue b/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
new file mode 100644
index 0000000..0883ec3
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
@@ -0,0 +1,418 @@
+<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>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue b/src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue
new file mode 100644
index 0000000..2e9d9a9
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue
@@ -0,0 +1,371 @@
+<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>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue b/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
new file mode 100644
index 0000000..3f0fc69
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
@@ -0,0 +1,416 @@
+<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>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue b/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue
new file mode 100644
index 0000000..c290be4
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue
@@ -0,0 +1,306 @@
+<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>
diff --git a/src/views/collaborativeApproval/notificationManagement/summary/index.vue b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
new file mode 100644
index 0000000..04eaa4a
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
@@ -0,0 +1,403 @@
+<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>
--
Gitblit v1.9.3