From 1b736033f6d01b774d58b4c2d7cd2ce8607a44fa Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期日, 28 十二月 2025 20:28:46 +0800
Subject: [PATCH] 页面完善

---
 src/views/OfficeRelated/conference/index.vue                               |  728 ++++++++++++++
 src/views/OfficeRelated/checkingIn/checkingInInfo.vue                      |  213 +--
 src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue |  680 ++++++++++---
 src/views/OfficeRelated/checkingIn/mockData.js                             |  169 --
 src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue       |  542 ---------
 src/views/OfficeRelated/engage/index.vue                                   |  691 +++++++++++++
 6 files changed, 2,120 insertions(+), 903 deletions(-)

diff --git a/src/views/OfficeRelated/checkingIn/checkingInInfo.vue b/src/views/OfficeRelated/checkingIn/checkingInInfo.vue
index f0c23c0..2ff3344 100644
--- a/src/views/OfficeRelated/checkingIn/checkingInInfo.vue
+++ b/src/views/OfficeRelated/checkingIn/checkingInInfo.vue
@@ -4,12 +4,18 @@
     <el-card class="employee-info-card">
       <div class="employee-header">
         <div class="employee-basic">
-          <el-avatar :size="60" :src="employeeInfo.avatar" class="employee-avatar">
+          <el-avatar
+            :size="60"
+            :src="employeeInfo.avatar"
+            class="employee-avatar"
+          >
             {{ employeeInfo.name.charAt(0) }}
           </el-avatar>
           <div class="employee-details">
             <h3>{{ employeeInfo.name }}</h3>
-            <p class="employee-department">{{ employeeInfo.department }} 路 {{ employeeInfo.position }}</p>
+            <p class="employee-department">
+              {{ employeeInfo.department }} 路 {{ employeeInfo.position }}
+            </p>
             <p class="employee-contact">
               <span>宸ュ彿: {{ employeeInfo.employeeId }}</span>
               <span>鐢佃瘽: {{ employeeInfo.phone }}</span>
@@ -36,13 +42,6 @@
     <!-- 閫夐」鍗� -->
     <el-card>
       <el-tabs v-model="activeTab">
-        <el-tab-pane label="鏃ュ巻瑙嗗浘" name="calendar">
-          <attendance-calendar
-            :attendance-data="attendanceData"
-            :business-trip-data="businessTripData"
-          />
-        </el-tab-pane>
-
         <el-tab-pane label="鍑哄嫟璁板綍" name="attendanceList">
           <personal-attendance-table
             :data="attendanceData"
@@ -56,7 +55,12 @@
             :loading="loading"
           />
         </el-tab-pane>
-
+        <el-tab-pane label="鏃ュ巻瑙嗗浘" name="calendar">
+          <attendance-calendar
+            :attendance-data="attendanceData"
+            :business-trip-data="businessTripData"
+          />
+        </el-tab-pane>
         <el-tab-pane label="缁熻鎶ヨ〃" name="report">
           <personal-attendance-report
             :stats="employeeStats"
@@ -69,15 +73,13 @@
 </template>
 
 <script>
-  import { generateMockData } from './mockData'
-
-import AttendanceCalendar from './components/AttendanceCalendar.vue'
-import PersonalAttendanceTable from './components/PersonalAttendanceTable.vue'
-import PersonBusiness from './components/PersonBusiness.vue'
-import PersonalAttendanceReport from './components/PersonalAttendanceReport.vue'
+import AttendanceCalendar from "./components/AttendanceCalendar.vue";
+import PersonalAttendanceTable from "./components/PersonalAttendanceTable.vue";
+import PersonBusiness from "./components/PersonBusiness.vue";
+import PersonalAttendanceReport from "./components/PersonalAttendanceReport.vue";
 
 export default {
-  name: 'AttendanceDetail',
+  name: "AttendanceDetail",
   components: {
     AttendanceCalendar,
     PersonalAttendanceTable,
@@ -86,15 +88,15 @@
   },
   data() {
     return {
-       activeTab: 'calendar',
+      activeTab: "calendar",
       loading: false,
       employeeInfo: {
-        name: '寮犱笁',
-        department: 'OPO椤圭洰閮�',
-        position: '椤圭洰缁忕悊',
-        employeeId: 'OPO001',
-        phone: '138-1234-5678',
-        avatar: ''
+        name: "",
+        department: "",
+        position: "",
+        employeeId: "",
+        phone: "",
+        avatar: ""
       },
       employeeStats: {
         attendanceRate: 0,
@@ -105,137 +107,120 @@
       },
       attendanceData: [],
       businessTripData: []
-    }
+    };
   },
   created() {
-        this.loadMockData()
-
-    this.getEmployeeInfo()
-    this.loadAttendanceData()
+    this.getEmployeeInfo();
+    this.loadAttendanceData();
   },
   methods: {
     getEmployeeInfo() {
-      const { employeeId, employeeName } = this.$route.query
+      const { employeeId, employeeName } = this.$route.query;
       // 妯℃嫙鍛樺伐淇℃伅
       this.employeeInfo = {
-        name: employeeName || '寮犱笁',
-        department: 'OPO椤圭洰閮�',
-        position: '椤圭洰缁忕悊',
-        employeeId: employeeId || 'OPO001',
-        phone: '138****1234',
-        avatar: ''
-      }
-    },
- loadMockData() {
-      this.loading = true
-
-      // 妯℃嫙寮傛鍔犺浇
-      setTimeout(() => {
-        const mockData = generateMockData()
-        this.attendanceData = mockData.attendanceData
-        this.businessTripData = mockData.businessTripData
-        this.calculateStats()
-        this.loading = false
-      }, 500)
+        name: employeeName || "寮犱笁",
+        department: "OPO椤圭洰閮�",
+        position: "椤圭洰缁忕悊",
+        employeeId: employeeId || "OPO001",
+        phone: "138****1234",
+        avatar: ""
+      };
     },
 
-    calculateStats() {
-      const totalDays = 31 // 12鏈堟湁31澶�
-      const attendanceDays = this.attendanceData.filter(item =>
-        item.status === 'present' || item.status === 'late'
-      ).length
-
-      const lateTimes = this.attendanceData.filter(item =>
-        item.status === 'late'
-      ).length
-
-      // 璁$畻鍑哄樊鎬诲ぉ鏁�
-      const businessTripDays = this.businessTripData.reduce((total, trip) => {
-        const start = new Date(trip.startDate)
-        const end = new Date(trip.endDate)
-        const days = Math.ceil((end - start) / (1000 * 60 * 60 * 24)) + 1
-        return total + days
-      }, 0)
-
-      // 璁$畻鎬诲伐浣滄椂闀�
-      const totalWorkHours = this.attendanceData.reduce((total, item) => {
-        return total + (item.workHours || 0)
-      }, 0)
-
-      this.employeeStats = {
-        attendanceRate: Math.round((attendanceDays / totalDays) * 100),
-        workHours: totalWorkHours.toFixed(1),
-        businessTripDays: businessTripDays,
-        lateTimes: lateTimes,
-        leaveEarlyTimes: this.attendanceData.filter(item =>
-          item.status === 'leaveEarly'
-        ).length
-      }
-    },
     async loadAttendanceData() {
-      this.loading = true
+      this.loading = true;
       try {
-        await new Promise(resolve => setTimeout(resolve, 500))
+        await new Promise(resolve => setTimeout(resolve, 500));
 
         // 鐢熸垚涓汉鑰冨嫟妯℃嫙鏁版嵁
-        this.attendanceData = this.generatePersonalAttendanceData()
-        this.businessTripData = this.generatePersonalBusinessTripData()
-        this.calculateStats()
+        this.attendanceData = this.generatePersonalAttendanceData();
+        this.businessTripData = this.generatePersonalBusinessTripData();
+        this.calculateStats();
       } catch (error) {
-        console.error('鍔犺浇鏁版嵁澶辫触:', error)
+        console.error("鍔犺浇鏁版嵁澶辫触:", error);
       } finally {
-        this.loading = false
+        this.loading = false;
       }
     },
 
     generatePersonalAttendanceData() {
-      const data = []
-      const currentMonth = 12 // 12鏈�
+      const data = [];
+      const currentMonth = 12; // 12鏈�
 
       for (let day = 1; day <= 31; day++) {
-        if (Math.random() > 0.2) { // 80%鐨勫嚭鍕ょ巼
+        if (Math.random() > 0.2) {
+          // 80%鐨勫嚭鍕ょ巼
           data.push({
             id: day,
-            date: `2024-${currentMonth.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`,
-            checkIn: `08:${String(Math.floor(Math.random() * 30)).padStart(2, '0')}`,
-            checkOut: `18:${String(Math.floor(Math.random() * 30)).padStart(2, '0')}`,
-            status: Math.random() > 0.1 ? '姝e父' : '杩熷埌',
+            date: `2024-${currentMonth
+              .toString()
+              .padStart(2, "0")}-${day.toString().padStart(2, "0")}`,
+            checkIn: `08:${String(Math.floor(Math.random() * 30)).padStart(
+              2,
+              "0"
+            )}`,
+            checkOut: `18:${String(Math.floor(Math.random() * 30)).padStart(
+              2,
+              "0"
+            )}`,
+            status: Math.random() > 0.1 ? "姝e父" : "杩熷埌",
             workHours: (8 + Math.random() * 2).toFixed(1)
-          })
+          });
         }
       }
-      return data
+      return data;
     },
 
     generatePersonalBusinessTripData() {
       return [
         {
           id: 1,
-          tripNumber: 'BT202412001',
-          startCity: '鍖椾含',
-          endCity: '涓婃捣',
-          startDate: '2024-12-05',
-          endDate: '2024-12-08',
+          tripNumber: "BT202412001",
+          startCity: "鍖椾含",
+          endCity: "涓婃捣",
+          startDate: "2024-12-05",
+          endDate: "2024-12-08",
           distance: 1200,
-          purpose: '瀹㈡埛浼氳',
-          status: '宸插畬鎴�'
+          purpose: "瀹㈡埛浼氳",
+          status: "宸插畬鎴�"
         },
         {
           id: 2,
-          tripNumber: 'BT202412002',
-          startCity: '鍖椾含',
-          endCity: '骞垮窞',
-          startDate: '2024-12-15',
-          endDate: '2024-12-18',
+          tripNumber: "BT202412002",
+          startCity: "鍖椾含",
+          endCity: "骞垮窞",
+          startDate: "2024-12-15",
+          endDate: "2024-12-18",
           distance: 1900,
-          purpose: '椤圭洰璋冪爺',
-          status: '宸插畬鎴�'
+          purpose: "椤圭洰璋冪爺",
+          status: "宸插畬鎴�"
         }
-      ]
+      ];
     },
 
+    calculateStats() {
+      const totalDays = 31;
+      const attendanceDays = this.attendanceData.length;
+
+      this.employeeStats = {
+        attendanceRate: Math.round((attendanceDays / totalDays) * 100),
+        workHours: this.attendanceData
+          .reduce((sum, item) => sum + parseFloat(item.workHours), 0)
+          .toFixed(1),
+        businessTripDays: this.businessTripData.reduce((sum, item) => {
+          const start = new Date(item.startDate);
+          const end = new Date(item.endDate);
+          return sum + Math.ceil((end - start) / (1000 * 60 * 60 * 24)) + 1;
+        }, 0),
+        lateTimes: this.attendanceData.filter(item => item.status === "杩熷埌")
+          .length,
+        leaveEarlyTimes: this.attendanceData.filter(
+          item => item.status === "鏃╅��"
+        ).length
+      };
+    }
   }
-}
+};
 </script>
 
 <style scoped>
diff --git a/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue b/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue
index c43976e..5f90278 100644
--- a/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue
+++ b/src/views/OfficeRelated/checkingIn/components/AttendanceCalendar.vue
@@ -1,132 +1,23 @@
 <template>
   <div class="attendance-calendar">
-    <!-- 鏃ュ巻澶撮儴缁熻淇℃伅 -->
-    <div class="calendar-stats">
-      <div class="stat-item">
-        <span class="stat-dot present"></span>
-        <span>姝e父鍑哄嫟: {{ stats.presentDays }}澶�</span>
-      </div>
-      <div class="stat-item">
-        <span class="stat-dot absent"></span>
-        <span>缂哄嫟/寮傚父: {{ stats.absentDays }}澶�</span>
-      </div>
-      <div class="stat-item">
-        <span class="stat-dot trip"></span>
-        <span>鍑哄樊: {{ stats.tripDays }}澶�</span>
-      </div>
-      <div class="stat-item">
-        <span class="stat-dot late"></span>
-        <span>杩熷埌/鏃╅��: {{ stats.lateDays }}澶�</span>
-      </div>
-    </div>
-
-    <!-- Element UI 鏃ュ巻缁勪欢 -->
-    <el-calendar v-model="calendarValue" :first-day-of-week="1">
+    <el-calendar v-model="calendarValue">
       <template #date-cell="{ data }">
-        <div
-          class="calendar-date"
-          :class="getDateStatusClass(data.day)"
-          @click="handleDateClick(data)"
-        >
+        <div class="calendar-date">
           <div class="date-number">{{ data.day.split('-')[2] }}</div>
-
-          <!-- 鐘舵�佽儗鏅壊鍧� -->
-          <div class="status-background" :class="getBackgroundStatus(data.day)"></div>
-
           <div class="date-events">
-            <!-- 鍑哄嫟鐘舵�佹爣璁� -->
-            <div v-if="getAttendanceStatus(data.day) !== 'absent'" class="status-mark">
-              <el-tooltip
-                :content="getAttendanceTooltip(data.day)"
-                placement="top"
-              >
-                <span class="status-dot" :class="getAttendanceStatus(data.day)"></span>
-              </el-tooltip>
-            </div>
-
-            <!-- 鍑哄樊鏍囪 -->
-            <div v-if="hasBusinessTrip(data.day)" class="trip-mark">
-              <el-tooltip content="鍑哄樊" placement="top">
-                <i class="el-icon-location-outline"></i>
-              </el-tooltip>
-            </div>
-
-            <!-- 缂哄嫟鏍囪 -->
-            <div v-if="getAttendanceStatus(data.day) === 'absent'" class="absent-mark">
-              <el-tooltip content="缂哄嫟" placement="top">
-                <i class="el-icon-close"></i>
-              </el-tooltip>
-            </div>
-          </div>
-
-          <!-- 绠�鐣ヤ俊鎭樉绀� -->
-          <div class="brief-info">
-            <div v-if="getAttendanceStatus(data.day) === 'present'" class="info-item present-info">
-              鈭�
-            </div>
-            <div v-else-if="getAttendanceStatus(data.day) === 'late'" class="info-item late-info">
-              !
-            </div>
-            <div v-else-if="getAttendanceStatus(data.day) === 'absent'" class="info-item absent-info">
-              脳
-            </div>
-            <div v-if="hasBusinessTrip(data.day)" class="info-item trip-info">
-              鉁�
-            </div>
-          </div>
-
-          <!-- 鏃ユ湡璇︾粏淇℃伅锛堟偓娴樉绀猴級 -->
-          <div class="date-details">
             <div
               v-for="event in getDateEvents(data.day)"
               :key="event.id"
-              class="detail-item"
+              class="event-item"
+              :class="event.type"
             >
-              <span class="detail-type">{{ event.type === 'attendance' ? '鍑哄嫟' : '鍑哄樊' }}</span>
-              <span class="detail-info">{{ event.text }}</span>
-            </div>
-            <div v-if="getDateEvents(data.day).length === 0" class="detail-item">
-              <span class="detail-type">鏃犺褰�</span>
+              <span class="event-dot"></span>
+              <span class="event-text">{{ event.text }}</span>
             </div>
           </div>
         </div>
       </template>
     </el-calendar>
-
-    <!-- 鏃ユ湡璇︽儏瀵硅瘽妗� -->
-    <el-dialog
-      :title="`${selectedDate} 鑰冨嫟璇︽儏`"
-      v-model="detailDialogVisible"
-      width="500px"
-    >
-      <div v-if="selectedDateInfo">
-        <div class="detail-section">
-          <h4>鍑哄嫟淇℃伅</h4>
-          <div v-if="selectedDateInfo.attendance">
-            <p>涓婄彮鏃堕棿: {{ selectedDateInfo.attendance.checkIn || '鏈墦鍗�' }}</p>
-            <p>涓嬬彮鏃堕棿: {{ selectedDateInfo.attendance.checkOut || '鏈墦鍗�' }}</p>
-            <p>鐘舵��:
-              <el-tag :type="getStatusTagType(selectedDateInfo.attendance.status)">
-                {{ getStatusText(selectedDateInfo.attendance.status) }}
-              </el-tag>
-            </p>
-          </div>
-          <div v-else>
-            <p class="no-data">鏃犲嚭鍕よ褰�</p>
-          </div>
-        </div>
-
-        <div class="detail-section" v-if="selectedDateInfo.businessTrip">
-          <h4>鍑哄樊淇℃伅</h4>
-          <p>鐩殑鍦�: {{ selectedDateInfo.businessTrip.destination }}</p>
-          <p>浜嬬敱: {{ selectedDateInfo.businessTrip.reason }}</p>
-          <p>閲岀▼: {{ selectedDateInfo.businessTrip.distance }}鍏噷</p>
-        </div>
-      </div>
-      <template #footer>
-        <el-button @click="detailDialogVisible = false">鍏抽棴</el-button>
-      </template>
-    </el-dialog>
   </div>
 </template>
 
@@ -134,211 +25,41 @@
 export default {
   name: 'AttendanceCalendar',
   props: {
-    attendanceData: {
-      type: Array,
-      default: () => []
-    },
-    businessTripData: {
-      type: Array,
-      default: () => []
-    }
+    attendanceData: Array,
+    businessTripData: Array
   },
   data() {
     return {
-      calendarValue: new Date(),
-      detailDialogVisible: false,
-      selectedDate: '',
-      selectedDateInfo: null,
-      stats: {
-        presentDays: 0,
-        absentDays: 0,
-        tripDays: 0,
-        lateDays: 0
-      }
-    }
-  },
-  mounted() {
-    this.calculateStats()
-  },
-  watch: {
-    attendanceData: {
-      handler() {
-        this.calculateStats()
-      },
-      deep: true
-    },
-    businessTripData: {
-      handler() {
-        this.calculateStats()
-      },
-      deep: true
+      calendarValue: new Date()
     }
   },
   methods: {
-    // 鑾峰彇鏃ユ湡鐘舵�佺被鍚�
-    getDateStatusClass(date) {
-      const classes = []
-      if (this.isToday(date)) {
-        classes.push('today')
-      }
-      if (this.isSelected(date)) {
-        classes.push('selected')
-      }
-
-      // 娣诲姞鐘舵�佺被鍚�
-      const status = this.getBackgroundStatus(date)
-      if (status) {
-        classes.push(status)
-      }
-
-      return classes
-    },
-
-    // 鑾峰彇鑳屾櫙鐘舵�侊紙鐢ㄤ簬鑳屾櫙鑹诧級
-    getBackgroundStatus(date) {
-      const attendance = this.attendanceData.find(item => item.date === date)
-      const hasTrip = this.hasBusinessTrip(date)
-
-      if (hasTrip) {
-        return 'has-trip'
-      }
-
-      if (attendance) {
-        switch (attendance.status) {
-          case 'present': return 'has-attendance'
-          case 'late': return 'has-late'
-          case 'absent': return 'has-absent'
-          default: return ''
-        }
-      }
-
-      return ''
-    },
-
-    // 鍒ゆ柇鏄惁涓轰粖澶�
-    isToday(date) {
-      const today = new Date()
-      const compareDate = new Date(date)
-      return today.toDateString() === compareDate.toDateString()
-    },
-
-    // 鍒ゆ柇鏄惁琚�変腑
-    isSelected(date) {
-      return this.selectedDate === date
-    },
-
-    // 鑾峰彇鑰冨嫟鐘舵��
-    getAttendanceStatus(date) {
-      const attendance = this.attendanceData.find(item => item.date === date)
-      if (!attendance) return 'absent'
-
-      switch (attendance.status) {
-        case 'present': return 'present'
-        case 'late': return 'late'
-        case 'absent': return 'absent'
-        default: return 'absent'
-      }
-    },
-
-    // 鑾峰彇鐘舵�佹枃鏈�
-    getStatusText(status) {
-      const statusMap = {
-        present: '姝e父鍑哄嫟',
-        late: '杩熷埌/鏃╅��',
-        absent: '缂哄嫟/寮傚父'
-      }
-      return statusMap[status] || '鏈煡鐘舵��'
-    },
-
-    // 鑾峰彇鑰冨嫟鐘舵�佹彁绀�
-    getAttendanceTooltip(date) {
-      const statusMap = {
-        present: '姝e父鍑哄嫟',
-        late: '杩熷埌/鏃╅��',
-        absent: '缂哄嫟/寮傚父'
-      }
-      return statusMap[this.getAttendanceStatus(date)] || '鏃犺褰�'
-    },
-
-    // 鍒ゆ柇鏄惁鏈夊嚭宸�
-    hasBusinessTrip(date) {
-      return this.businessTripData.some(item =>
-        date >= item.startDate && date <= item.endDate
-      )
-    },
-
-    // 鑾峰彇鏃ユ湡浜嬩欢
     getDateEvents(date) {
       const events = []
-      const attendance = this.attendanceData.find(item => item.date === date)
 
+      // 妫�鏌ュ嚭鍕よ褰�
+      const attendance = this.attendanceData.find(item => item.date === date)
       if (attendance) {
         events.push({
           id: `attendance-${date}`,
           type: 'attendance',
-          text: `${attendance.checkIn || '鏈墦鍗�'} - ${attendance.checkOut || '鏈墦鍗�'}`
+          text: `${attendance.checkIn}-${attendance.checkOut}`
         })
       }
 
+      // 妫�鏌ュ嚭宸褰�
       const businessTrip = this.businessTripData.find(item =>
         date >= item.startDate && date <= item.endDate
       )
       if (businessTrip) {
         events.push({
           id: `business-trip-${date}`,
-          type: 'businessTrip',
-          text: `鍓嶅線${businessTrip.destination}`
+          type: 'business-trip',
+          text: `鍑哄樊: ${businessTrip.endCity}`
         })
       }
 
       return events
-    },
-
-    // 澶勭悊鏃ユ湡鐐瑰嚮浜嬩欢
-    handleDateClick(data) {
-      this.selectedDate = data.day
-      this.selectedDateInfo = {
-        attendance: this.attendanceData.find(item => item.date === data.day),
-        businessTrip: this.businessTripData.find(item =>
-          data.day >= item.startDate && data.day <= item.endDate
-        )
-      }
-      this.detailDialogVisible = true
-    },
-
-    // 鑾峰彇鐘舵�佹爣绛剧被鍨�
-    getStatusTagType(status) {
-      const typeMap = {
-        present: 'success',
-        late: 'warning',
-        absent: 'danger'
-      }
-      return typeMap[status] || 'info'
-    },
-
-    // 璁$畻缁熻淇℃伅
-    calculateStats() {
-      // 閲嶇疆缁熻
-      this.stats = { presentDays: 0, absentDays: 0, tripDays: 0, lateDays: 0 }
-
-      this.attendanceData.forEach(item => {
-        switch (item.status) {
-          case 'present': this.stats.presentDays++; break
-          case 'late': this.stats.lateDays++; break
-          case 'absent': this.stats.absentDays++; break
-        }
-      })
-
-      // 璁$畻鍑哄樊澶╂暟
-      const tripDays = new Set()
-      this.businessTripData.forEach(item => {
-        const start = new Date(item.startDate)
-        const end = new Date(item.endDate)
-        for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
-          tripDays.add(d.toISOString().split('T')[0])
-        }
-      })
-      this.stats.tripDays = tripDays.size
     }
   }
 }
@@ -347,242 +68,49 @@
 <style scoped>
 .attendance-calendar {
   padding: 20px;
-  max-width: 100%;
 }
-
-.calendar-stats {
-  display: flex;
-  justify-content: space-around;
-  margin-bottom: 20px;
-  padding: 15px;
-  background: #f5f7fa;
-  border-radius: 8px;
-}
-
-.stat-item {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-}
-
-.stat-dot {
-  width: 12px;
-  height: 12px;
-  border-radius: 50%;
-  display: inline-block;
-}
-
-.stat-dot.present { background-color: #67c23a; }
-.stat-dot.absent { background-color: #f56c6c; }
-.stat-dot.trip { background-color: #409eff; }
-.stat-dot.late { background-color: #e6a23c; }
 
 .calendar-date {
-  height: 80px;
-  padding: 4px;
-  border: 1px solid #ebeef5;
-  border-radius: 4px;
-  cursor: pointer;
-  transition: all 0.3s;
-  position: relative;
-  overflow: hidden;
-}
-
-.calendar-date:hover {
-  background-color: #f0f9ff;
-  border-color: #409eff;
-  transform: translateY(-2px);
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-}
-
-.calendar-date.today {
-  border-color: #409eff;
-  background-color: #f0f9ff;
-}
-
-.calendar-date.selected {
-  background-color: #ecf5ff;
-  border-color: #409eff;
-}
-
-/* 鐘舵�佽儗鏅壊 */
-.status-background {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  opacity: 0.1;
-  z-index: 0;
-}
-
-.calendar-date.has-attendance .status-background {
-  background-color: #67c23a;
-}
-
-.calendar-date.has-late .status-background {
-  background-color: #e6a23c;
-}
-
-.calendar-date.has-absent .status-background {
-  background-color: #f56c6c;
-}
-
-.calendar-date.has-trip .status-background {
-  background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
+  height: 100%;
+  display: flex;
+  flex-direction: column;
 }
 
 .date-number {
   font-weight: bold;
-  font-size: 14px;
-  margin-bottom: 2px;
-  position: relative;
-  z-index: 1;
+  margin-bottom: 4px;
 }
 
 .date-events {
-  display: flex;
-  flex-direction: column;
-  gap: 2px;
-  position: relative;
-  z-index: 1;
+  flex: 1;
+  overflow: hidden;
 }
 
-.status-mark, .trip-mark, .absent-mark {
+.event-item {
   display: flex;
   align-items: center;
-  gap: 4px;
+  margin-bottom: 2px;
+  font-size: 12px;
 }
 
-.status-dot {
-  width: 8px;
-  height: 8px;
+.event-dot {
+  width: 6px;
+  height: 6px;
   border-radius: 50%;
-  display: inline-block;
-}
-
-.status-dot.present { background-color: #67c23a; }
-.status-dot.late { background-color: #e6a23c; }
-.status-dot.absent { background-color: #f56c6c; }
-
-.trip-mark i {
-  color: #409eff;
-  font-size: 12px;
-}
-
-.absent-mark i {
-  color: #f56c6c;
-  font-size: 12px;
-}
-
-/* 绠�鐣ヤ俊鎭樉绀� */
-.brief-info {
-  position: absolute;
-  bottom: 4px;
-  right: 4px;
-  display: flex;
-  gap: 2px;
-  z-index: 1;
-}
-
-.info-item {
-  width: 16px;
-  height: 16px;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 10px;
-  font-weight: bold;
-}
-
-.present-info {
-  background-color: #67c23a;
-  color: white;
-}
-
-.late-info {
-  background-color: #e6a23c;
-  color: white;
-}
-
-.absent-info {
-  background-color: #f56c6c;
-  color: white;
-}
-
-.trip-info {
-  background-color: #409eff;
-  color: white;
-}
-
-.date-details {
-  position: absolute;
-  top: 100%;
-  left: 0;
-  right: 0;
-  background: white;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-  padding: 8px;
-  z-index: 1000;
-  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-  display: none;
-}
-
-.calendar-date:hover .date-details {
-  display: block;
-}
-
-.detail-item {
-  font-size: 12px;
-  margin-bottom: 4px;
-  display: flex;
-  align-items: center;
-}
-
-.detail-type {
-  font-weight: bold;
   margin-right: 4px;
-  min-width: 40px;
 }
 
-.detail-info {
-  color: #606266;
+.event-item.attendance .event-dot {
+  background-color: #67c23a;
 }
 
-.detail-section {
-  margin-bottom: 20px;
+.event-item.business-trip .event-dot {
+  background-color: #409eff;
 }
 
-.detail-section h4 {
-  margin-bottom: 10px;
-  color: #303133;
-  border-left: 4px solid #409eff;
-  padding-left: 8px;
-}
-
-.no-data {
-  color: #909399;
-  font-style: italic;
-}
-
-:deep(.el-calendar__header) {
-  padding: 10px;
-  border-bottom: 1px solid #ebeef5;
-}
-
-:deep(.el-calendar-day) {
-  padding: 0 !important;
-  height: 80px;
-}
-
-:deep(.el-calendar-table:not(.is-range) td) {
-  border: 1px solid #f0f0f0;
-}
-
-:deep(.el-calendar-table .el-calendar-day) {
-  height: 80px !important;
-  padding: 0 !important;
+.event-text {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 </style>
diff --git a/src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue b/src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue
index 8c5d0bd..0771f71 100644
--- a/src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue
+++ b/src/views/OfficeRelated/checkingIn/components/PersonalAttendanceReport.vue
@@ -12,6 +12,7 @@
           size="small"
           @click="exportReport"
           icon="el-icon-download"
+          type="primary"
         >
           瀵煎嚭鎶ヨ〃
         </el-button>
@@ -20,7 +21,7 @@
 
     <!-- 缁熻姒傝 -->
     <el-row :gutter="20" class="stats-overview">
-      <el-col :span="6">
+      <el-col :xs="12" :sm="6" class="stat-col">
         <el-card shadow="hover" class="stat-card">
           <div class="stat-content">
             <div class="stat-icon attendance-icon">
@@ -33,7 +34,7 @@
           </div>
         </el-card>
       </el-col>
-      <el-col :span="6">
+      <el-col :xs="12" :sm="6" class="stat-col">
         <el-card shadow="hover" class="stat-card">
           <div class="stat-content">
             <div class="stat-icon present-icon">
@@ -46,7 +47,7 @@
           </div>
         </el-card>
       </el-col>
-      <el-col :span="6">
+      <el-col :xs="12" :sm="6" class="stat-col">
         <el-card shadow="hover" class="stat-card">
           <div class="stat-content">
             <div class="stat-icon abnormal-icon">
@@ -59,7 +60,7 @@
           </div>
         </el-card>
       </el-col>
-      <el-col :span="6">
+      <el-col :xs="12" :sm="6" class="stat-col">
         <el-card shadow="hover" class="stat-card">
           <div class="stat-content">
             <div class="stat-icon rate-icon">
@@ -74,98 +75,150 @@
       </el-col>
     </el-row>
 
-    <!-- 鍥捐〃鍖哄煙 -->
+    <!-- 鍥捐〃鍖哄煙 - 浼樺寲甯冨眬 -->
     <el-row :gutter="20" class="charts-section">
-      <el-col :span="12">
-        <el-card header="鍑哄嫟瓒嬪娍" shadow="never">
+      <el-col :xs="24" :lg="12" class="chart-col">
+        <el-card class="chart-card" shadow="never">
+          <template #header>
+            <div class="chart-header">
+              <span class="chart-title">鍑哄嫟瓒嬪娍鍒嗘瀽</span>
+              <div class="chart-legend">
+                <span class="legend-item">
+                  <span class="legend-color bar-color"></span>
+                  鍑哄嫟澶╂暟
+                </span>
+                <span class="legend-item">
+                  <span class="legend-color line-color"></span>
+                  鍑哄嫟鐜�
+                </span>
+              </div>
+            </div>
+          </template>
           <div id="attendanceTrendChart" class="chart-container"></div>
         </el-card>
       </el-col>
-      <el-col :span="12">
-        <el-card header="鑰冨嫟鍒嗗竷" shadow="never">
+      <el-col :xs="24" :lg="12" class="chart-col">
+        <el-card class="chart-card" shadow="never">
+          <template #header>
+            <div class="chart-header">
+              <span class="chart-title">鑰冨嫟鍒嗗竷</span>
+            </div>
+          </template>
           <div id="attendanceDistributionChart" class="chart-container"></div>
         </el-card>
       </el-col>
     </el-row>
 
     <!-- 璇︾粏缁熻琛ㄦ牸 -->
-    <el-card header="璇︾粏缁熻" class="detail-table-card" shadow="never">
-      <el-table :data="detailedStats" border style="width: 100%">
-        <el-table-column prop="month" label="鏈堜唤"  />
-        <el-table-column prop="workDays" label="搴斿嚭鍕ゅぉ鏁�"  />
-        <el-table-column prop="actualDays" label="瀹為檯鍑哄嫟"  />
-        <el-table-column prop="lateTimes" label="杩熷埌娆℃暟" >
-          <template #default="scope">
-            <el-tag v-if="scope.row.lateTimes > 0" type="warning" size="small">
-              {{ scope.row.lateTimes }}
-            </el-tag>
-            <span v-else>{{ scope.row.lateTimes }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="leaveEarlyTimes" label="鏃╅��娆℃暟" >
-          <template #default="scope">
-            <el-tag v-if="scope.row.leaveEarlyTimes > 0" type="warning" size="small">
-              {{ scope.row.leaveEarlyTimes }}
-            </el-tag>
-            <span v-else>{{ scope.row.leaveEarlyTimes }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="absenceDays" label="缂哄嫟澶╂暟" >
-          <template #default="scope">
-            <el-tag v-if="scope.row.absenceDays > 0" type="danger" size="small">
-              {{ scope.row.absenceDays }}
-            </el-tag>
-            <span v-else>{{ scope.row.absenceDays }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="businessTripDays" label="鍑哄樊澶╂暟" >
-          <template #default="scope">
-            <el-tag v-if="scope.row.businessTripDays > 0" type="primary" size="small">
-              {{ scope.row.businessTripDays }}
-            </el-tag>
-            <span v-else>{{ scope.row.businessTripDays }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="attendanceRate" label="鍑哄嫟鐜�" >
-          <template #default="scope">
-            <el-progress
-              :percentage="scope.row.attendanceRate"
-              :show-text="false"
-              :color="getProgressColor(scope.row.attendanceRate)"
-            />
-            <span>{{ scope.row.attendanceRate }}%</span>
-          </template>
-        </el-table-column>
-      </el-table>
+    <el-card class="detail-card" shadow="never">
+      <template #header>
+        <div class="card-header">
+          <span class="card-title">鏈堝害璇︾粏缁熻</span>
+          <el-button size="mini" type="text" @click="toggleTableExpand">
+            {{ tableExpanded ? '鏀惰捣' : '灞曞紑' }}
+            <i :class="tableExpanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
+          </el-button>
+        </div>
+      </template>
+      <el-collapse-transition>
+        <div v-show="tableExpanded">
+          <el-table
+            :data="detailedStats"
+            border
+            style="width: 100%"
+            class="stats-table"
+            :row-class-name="getRowClassName"
+          >
+            <!-- 琛ㄦ牸鍒楀畾涔変繚鎸佷笉鍙� -->
+            <el-table-column prop="month" label="鏈堜唤" align="center" />
+            <el-table-column prop="workDays" label="搴斿嚭鍕ゅぉ鏁�" align="center" />
+            <el-table-column prop="actualDays" label="瀹為檯鍑哄嫟" align="center" />
+            <el-table-column prop="lateTimes" label="杩熷埌娆℃暟" align="center">
+              <template #default="scope">
+                <el-tag v-if="scope.row.lateTimes > 0" type="warning" size="small">
+                  {{ scope.row.lateTimes }}
+                </el-tag>
+                <span v-else>{{ scope.row.lateTimes }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="leaveEarlyTimes" label="鏃╅��娆℃暟" align="center">
+              <template #default="scope">
+                <el-tag v-if="scope.row.leaveEarlyTimes > 0" type="warning" size="small">
+                  {{ scope.row.leaveEarlyTimes }}
+                </el-tag>
+                <span v-else>{{ scope.row.leaveEarlyTimes }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="absenceDays" label="缂哄嫟澶╂暟" align="center">
+              <template #default="scope">
+                <el-tag v-if="scope.row.absenceDays > 0" type="danger" size="small">
+                  {{ scope.row.absenceDays }}
+                </el-tag>
+                <span v-else>{{ scope.row.absenceDays }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="businessTripDays" label="鍑哄樊澶╂暟" align="center">
+              <template #default="scope">
+                <el-tag v-if="scope.row.businessTripDays > 0" type="primary" size="small">
+                  {{ scope.row.businessTripDays }}
+                </el-tag>
+                <span v-else>{{ scope.row.businessTripDays }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="attendanceRate" label="鍑哄嫟鐜�" align="center" min-width="100">
+              <template #default="scope">
+                <div class="progress-cell">
+                  <el-progress
+                    :percentage="scope.row.attendanceRate"
+                    :show-text="false"
+                    :color="getProgressColor(scope.row.attendanceRate)"
+                    class="rate-progress"
+                  />
+                  <span class="rate-text" :class="getRateTextClass(scope.row.attendanceRate)">
+                    {{ scope.row.attendanceRate }}%
+                  </span>
+                </div>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </el-collapse-transition>
     </el-card>
 
     <!-- 寮傚父璁板綍 -->
-    <el-card header="寮傚父璁板綍" class="abnormal-records-card" shadow="never">
-      <el-table :data="abnormalRecords" border style="width: 100%">
-        <el-table-column prop="date" label="鏃ユ湡"  />
-        <el-table-column prop="type" label="寮傚父绫诲瀷" >
+    <el-card class="abnormal-card" shadow="never">
+      <template #header>
+        <div class="card-header">
+          <span class="card-title">寮傚父璁板綍鏄庣粏</span>
+        </div>
+      </template>
+      <el-table :data="abnormalRecords" border class="abnormal-table">
+        <!-- 寮傚父璁板綍琛ㄦ牸鍒楀畾涔変繚鎸佷笉鍙� -->
+        <el-table-column prop="date" label="鏃ユ湡" align="center" />
+        <el-table-column prop="type" label="寮傚父绫诲瀷" align="center">
           <template #default="scope">
-            <el-tag :type="getAbnormalType(scope.row.type)" size="small">
+            <el-tag :type="getAbnormalType(scope.row.type)" size="small" effect="light">
               {{ scope.row.type }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="description" label="鎻忚堪" min-width="200" />
-        <el-table-column prop="duration" label="鏃堕暱"  />
-        <el-table-column prop="status" label="鐘舵��" >
+        <el-table-column prop="description" label="鎻忚堪" align="center" min-width="200" />
+        <el-table-column prop="duration" label="鏃堕暱" align="center" />
+        <el-table-column prop="status" label="鐘舵��" align="center">
           <template #default="scope">
             <el-tag
               :type="scope.row.status === '宸插鐞�' ? 'success' : 'warning'"
               size="small"
+              effect="light"
             >
               {{ scope.row.status }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="鎿嶄綔" >
+        <el-table-column label="鎿嶄綔" align="center" width="80">
           <template #default="scope">
             <el-button type="text" size="mini" @click="viewAbnormalDetail(scope.row)">
-              鏌ョ湅
+              璇︽儏
             </el-button>
           </template>
         </el-table-column>
@@ -201,7 +254,8 @@
       detailedStats: [],
       abnormalRecords: [],
       trendChart: null,
-      distributionChart: null
+      distributionChart: null,
+      tableExpanded: true
     }
   },
   mounted() {
@@ -211,7 +265,7 @@
     })
   },
   beforeDestroy() {
-    // 閿�姣佸浘琛ㄥ疄渚�
+    // 閿�姣佸浘琛ㄥ疄渚媅3](@ref)
     if (this.trendChart) {
       this.trendChart.dispose()
     }
@@ -221,7 +275,7 @@
   },
   methods: {
     initData() {
-      // 鍒濆鍖栨瑙堟暟鎹�
+      // 鍒濆鍖栨暟鎹�昏緫淇濇寔涓嶅彉
       this.overview = {
         totalDays: this.stats.totalDays || 22,
         presentDays: this.stats.presentDays || 20,
@@ -229,7 +283,6 @@
         attendanceRate: this.stats.attendanceRate || 90.9
       }
 
-      // 鍒濆鍖栬缁嗙粺璁�
       this.detailedStats = [
         { month: '2024-12', workDays: 22, actualDays: 20, lateTimes: 2,
           leaveEarlyTimes: 1, absenceDays: 0, businessTripDays: 3, attendanceRate: 90.9 },
@@ -239,7 +292,6 @@
           leaveEarlyTimes: 0, absenceDays: 0, businessTripDays: 1, attendanceRate: 95.7 }
       ]
 
-      // 鍒濆鍖栧紓甯歌褰�
       this.abnormalRecords = [
         { date: '2024-12-15', type: '杩熷埌', description: '鏃╀笂杩熷埌30鍒嗛挓', duration: '30鍒嗛挓', status: '宸插鐞�' },
         { date: '2024-12-08', type: '鏃╅��', description: '涓嬪崍鎻愬墠1灏忔椂绂诲紑', duration: '1灏忔椂', status: '宸插鐞�' },
@@ -261,43 +313,89 @@
       const option = {
         tooltip: {
           trigger: 'axis',
+          backgroundColor: 'rgba(255, 255, 255, 0.95)',
+          borderColor: '#ebeef5',
+          borderWidth: 1,
+          textStyle: {
+            color: '#606266'
+          },
           formatter: function(params) {
-            let result = params[0].axisValue + '<br/>'
+            let result = `<div style="font-weight: 600; margin-bottom: 8px;">${params[0].axisValue}</div>`
             params.forEach(param => {
-              result += `${param.seriesName}: ${param.value}<br/>`
+              const icon = param.seriesType === 'bar' ? '鈼�' : '鈼�'
+              result += `<div>${icon} ${param.seriesName}: <span style="font-weight: 600; color: ${param.color}">${param.value}${param.seriesName === '鍑哄嫟鐜�' ? '%' : ''}</span></div>`
             })
             return result
           }
         },
         legend: {
           data: ['鍑哄嫟澶╂暟', '寮傚父澶╂暟', '鍑哄嫟鐜�'],
-          bottom: 10
+          bottom: 10,
+          textStyle: {
+            color: '#606266'
+          },
+          itemWidth: 12,
+          itemHeight: 12
         },
         grid: {
           left: '3%',
-          right: '4%',
+          right: '3%',
           bottom: '15%',
-          top: '10%',
+          top: '15%',
           containLabel: true
         },
         xAxis: {
           type: 'category',
-          data: ['10鏈�', '11鏈�', '12鏈�']
+          data: ['10鏈�', '11鏈�', '12鏈�'],
+          axisLine: {
+            lineStyle: {
+              color: '#dcdfe6'
+            }
+          },
+          axisLabel: {
+            color: '#606266'
+          }
         },
         yAxis: [
           {
             type: 'value',
             name: '澶╂暟',
             min: 0,
-            max: 30
+            max: 30,
+            axisLine: {
+              show: true,
+              lineStyle: {
+                color: '#dcdfe6'
+              }
+            },
+            axisLabel: {
+              color: '#606266',
+              formatter: '{value}'
+            },
+            splitLine: {
+              lineStyle: {
+                color: '#f0f2f5',
+                type: 'dashed'
+              }
+            }
           },
           {
             type: 'value',
             name: '鍑哄嫟鐜�(%)',
             min: 0,
             max: 100,
+            axisLine: {
+              show: true,
+              lineStyle: {
+                color: '#dcdfe6'
+              }
+            },
             axisLabel: {
+              color: '#606266',
               formatter: '{value}%'
+            },
+            splitLine: {
+              show: false
             }
           }
         ],
@@ -305,19 +403,39 @@
           {
             name: '鍑哄嫟澶╂暟',
             type: 'bar',
-            barWidth: '30%',
+            barWidth: '25%',
             data: [22, 19, 20],
             itemStyle: {
-              color: '#409EFF'
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                { offset: 0, color: '#409EFF' },
+                { offset: 1, color: '#66b1ff' }
+              ]),
+              borderRadius: [2, 2, 0, 0]
+            },
+            emphasis: {
+              itemStyle: {
+                shadowBlur: 10,
+                shadowColor: 'rgba(64, 158, 255, 0.5)'
+              }
             }
           },
           {
             name: '寮傚父澶╂暟',
             type: 'bar',
-            barWidth: '30%',
+            barWidth: '25%',
             data: [1, 2, 2],
             itemStyle: {
-              color: '#F56C6C'
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                { offset: 0, color: '#F56C6C' },
+                { offset: 1, color: '#f78989' }
+              ]),
+              borderRadius: [2, 2, 0, 0]
+            },
+            emphasis: {
+              itemStyle: {
+                shadowBlur: 10,
+                shadowColor: 'rgba(245, 108, 108, 0.5)'
+              }
             }
           },
           {
@@ -325,14 +443,28 @@
             type: 'line',
             yAxisIndex: 1,
             data: [95.7, 90.5, 90.9],
+            symbol: 'circle',
+            symbolSize: 8,
             itemStyle: {
-              color: '#67C23A'
+              color: '#67C23A',
+              borderColor: '#fff',
+              borderWidth: 2
             },
             lineStyle: {
-              width: 3
-            }
+              width: 3,
+              shadowColor: 'rgba(103, 194, 58, 0.3)',
+              shadowBlur: 8,
+              shadowOffsetY: 2
+            },
+            emphasis: {
+              scale: true
+            },
+            animationEasing: 'cubicInOut',
+            animationDuration: 2000
           }
-        ]
+        ],
+        animation: true,
+        animationDuration: 1500
       }
       this.trendChart.setOption(option)
     },
@@ -345,13 +477,27 @@
       const option = {
         tooltip: {
           trigger: 'item',
-          formatter: '{a} <br/>{b}: {c} ({d}%)'
+          backgroundColor: 'rgba(255, 255, 255, 0.95)',
+          borderColor: '#ebeef5',
+          borderWidth: 1,
+          textStyle: {
+            color: '#606266'
+          },
+          formatter: '{a} <br/>{b}: {c}澶� ({d}%)'
         },
         legend: {
           orient: 'vertical',
           right: 10,
           top: 'center',
-          data: ['姝e父鍑哄嫟', '杩熷埌', '鏃╅��', '缂哄嫟', '鍑哄樊']
+          textStyle: {
+            color: '#606266',
+            fontSize: 12
+          },
+          itemWidth: 12,
+          itemHeight: 12,
+          formatter: function(name) {
+            return `${name}`
+          }
         },
         series: [
           {
@@ -359,24 +505,38 @@
             type: 'pie',
             radius: ['40%', '70%'],
             center: ['40%', '50%'],
-            avoidLabelOverlap: false,
+            avoidLabelOverlap: true,
             itemStyle: {
               borderColor: '#fff',
-              borderWidth: 2
+              borderWidth: 2,
+              borderRadius: 3,
+              shadowBlur: 5,
+              shadowColor: 'rgba(0, 0, 0, 0.1)'
             },
             label: {
-              show: false,
-              position: 'center'
+              show: true,
+              formatter: '{b}\n{d}%',
+              textStyle: {
+                fontSize: 12,
+                fontWeight: 'normal'
+              }
             },
             emphasis: {
               label: {
                 show: true,
-                fontSize: 18,
+                fontSize: 14,
                 fontWeight: 'bold'
+              },
+              itemStyle: {
+                shadowBlur: 10,
+                shadowOffsetX: 0,
+                shadowColor: 'rgba(0, 0, 0, 0.2)'
               }
             },
             labelLine: {
-              show: false
+              length: 15,
+              length2: 10,
+              smooth: true
             },
             data: [
               { value: 20, name: '姝e父鍑哄嫟', itemStyle: { color: '#67C23A' } },
@@ -384,23 +544,30 @@
               { value: 1, name: '鏃╅��', itemStyle: { color: '#F56C6C' } },
               { value: 0, name: '缂哄嫟', itemStyle: { color: '#909399' } },
               { value: 3, name: '鍑哄樊', itemStyle: { color: '#409EFF' } }
-            ]
+            ],
+            animationType: 'scale',
+            animationEasing: 'elasticOut',
+            animationDelay: function(idx) {
+              return Math.random() * 200
+            }
           }
-        ]
+        ],
+        animation: true,
+        animationDuration: 1000
       }
       this.distributionChart.setOption(option)
     },
 
     setupChartResize() {
-      // 鐩戝惉绐楀彛鍙樺寲锛岄噸鏂版覆鏌撳浘琛�
-      const handleResize = () => {
+      // 浣跨敤鑺傛祦鍑芥暟浼樺寲鎬ц兘[3](@ref)
+      const handleResize = this.throttle(() => {
         if (this.trendChart) {
           this.trendChart.resize()
         }
         if (this.distributionChart) {
           this.distributionChart.resize()
         }
-      }
+      }, 300)
 
       window.addEventListener('resize', handleResize)
       this.$once('hook:beforeDestroy', () => {
@@ -408,13 +575,42 @@
       })
     },
 
+    throttle(func, wait) {
+      let timeout = null
+      return function() {
+        const context = this
+        const args = arguments
+        if (!timeout) {
+          timeout = setTimeout(() => {
+            timeout = null
+            func.apply(context, args)
+          }, wait)
+        }
+      }
+    },
+
+    toggleTableExpand() {
+      this.tableExpanded = !this.tableExpanded
+    },
+
+    getRowClassName({ rowIndex }) {
+      return rowIndex % 2 === 1 ? 'even-row' : 'odd-row'
+    },
+
+    getRateTextClass(rate) {
+      if (rate >= 95) return 'rate-excellent'
+      if (rate >= 90) return 'rate-good'
+      if (rate >= 80) return 'rate-average'
+      return 'rate-poor'
+    },
+
+    // 鍏朵粬鏂规硶淇濇寔涓嶅彉
     handlePeriodChange(period) {
       this.reportPeriod = period
       this.updateChartData()
     },
 
     updateChartData() {
-      // 鏍规嵁閫夋嫨鐨勫懆鏈熸洿鏂板浘琛ㄦ暟鎹�
       let data
       switch (this.reportPeriod) {
         case 'month':
@@ -429,12 +625,10 @@
         default:
           data = this.getMonthlyData()
       }
-
       this.updateCharts(data)
     },
 
     getMonthlyData() {
-      // 妯℃嫙鏈堝害鏁版嵁
       return {
         xAxis: ['10鏈�', '11鏈�', '12鏈�'],
         attendance: [22, 19, 20],
@@ -444,7 +638,6 @@
     },
 
     getQuarterlyData() {
-      // 妯℃嫙瀛e害鏁版嵁
       return {
         xAxis: ['Q1', 'Q2', 'Q3', 'Q4'],
         attendance: [65, 62, 58, 61],
@@ -454,7 +647,6 @@
     },
 
     getYearlyData() {
-      // 妯℃嫙骞村害鏁版嵁
       return {
         xAxis: ['2022', '2023', '2024'],
         attendance: [240, 248, 252],
@@ -470,7 +662,7 @@
         option.series[0].data = data.attendance
         option.series[1].data = data.abnormal
         option.series[2].data = data.rate
-        this.trendChart.setOption(option)
+        this.trendChart.setOption(option, { notMerge: false })
       }
     },
 
@@ -493,12 +685,10 @@
 
     viewAbnormalDetail(record) {
       this.$message.info(`鏌ョ湅寮傚父璁板綍: ${record.date} - ${record.type}`)
-      // 杩欓噷鍙互鎵撳紑璇︽儏瀵硅瘽妗�
     },
 
     exportReport() {
       this.$message.success('鎶ヨ〃瀵煎嚭鍔熻兘寮�鍙戜腑')
-      // 杩欓噷鍙互瀹炵幇瀵煎嚭PDF鎴朎xcel鍔熻兘
     }
   }
 }
@@ -506,9 +696,9 @@
 
 <style scoped>
 .personal-attendance-report {
-  padding: 20px;
-  background: #fff;
-  border-radius: 8px;
+  padding: 24px;
+  background: #f8f9fa;
+  min-height: 100vh;
 }
 
 .report-header {
@@ -516,15 +706,17 @@
   justify-content: space-between;
   align-items: center;
   margin-bottom: 24px;
-  padding-bottom: 16px;
-  border-bottom: 1px solid #ebeef5;
+  padding: 0;
 }
 
 .report-header h4 {
   margin: 0;
   color: #303133;
-  font-size: 20px;
+  font-size: 24px;
   font-weight: 600;
+  background: linear-gradient(135deg, #409EFF, #67C23A);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
 }
 
 .header-actions {
@@ -534,51 +726,78 @@
 }
 
 .stats-overview {
-  margin-bottom: 24px;
+  margin-bottom: 32px;
+}
+
+.stat-col {
+  margin-bottom: 20px;
 }
 
 .stat-card {
-  border-radius: 8px;
-  transition: all 0.3s ease;
+  border-radius: 12px;
+  border: none;
+  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+  background: #fff;
+  position: relative;
+  overflow: hidden;
+}
+
+.stat-card::before {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  height: 3px;
+  background: linear-gradient(90deg, var(--gradient-start), var(--gradient-end));
 }
 
 .stat-card:hover {
-  transform: translateY(-2px);
-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  transform: translateY(-4px);
+  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15);
 }
 
 .stat-content {
   display: flex;
   align-items: center;
-  padding: 16px;
+  padding: 20px;
 }
 
 .stat-icon {
-  width: 60px;
-  height: 60px;
-  border-radius: 50%;
+  width: 64px;
+  height: 64px;
+  border-radius: 16px;
   display: flex;
   align-items: center;
   justify-content: center;
   margin-right: 16px;
-  font-size: 24px;
+  font-size: 28px;
   color: white;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
 }
 
 .attendance-icon {
-  background: linear-gradient(135deg, #409EFF, #79BBFF);
+  --gradient-start: #409EFF;
+  --gradient-end: #66b1ff;
+  background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
 }
 
 .present-icon {
-  background: linear-gradient(135deg, #67C23A, #95D475);
+  --gradient-start: #67C23A;
+  --gradient-end: #85ce61;
+  background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
 }
 
 .abnormal-icon {
-  background: linear-gradient(135deg, #E6A23C, #EEBD6D);
+  --gradient-start: #E6A23C;
+  --gradient-end: #ebb563;
+  background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
 }
 
 .rate-icon {
-  background: linear-gradient(135deg, #F56C6C, #F89898);
+  --gradient-start: #F56C6C;
+  --gradient-end: #f78989;
+  background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
 }
 
 .stat-info {
@@ -586,47 +805,162 @@
 }
 
 .stat-value {
-  font-size: 28px;
-  font-weight: bold;
+  font-size: 32px;
+  font-weight: 700;
   color: #303133;
   margin-bottom: 4px;
+  line-height: 1;
 }
 
 .stat-label {
   color: #909399;
   font-size: 14px;
+  font-weight: 500;
 }
 
 .charts-section {
+  margin-bottom: 32px;
+}
+
+.chart-col {
   margin-bottom: 24px;
 }
 
-.chart-container {
-  width: 100%;
-  height: 300px;
+.chart-card {
+  border-radius: 12px;
+  border: none;
+  background: #fff;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
 }
 
-.detail-table-card,
-.abnormal-records-card {
-  margin-bottom: 20px;
+.chart-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 0;
+}
+
+.chart-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.chart-legend {
+  display: flex;
+  gap: 16px;
+  align-items: center;
+}
+
+.legend-item {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  font-size: 12px;
+  color: #606266;
+}
+
+.legend-color {
+  width: 12px;
+  height: 12px;
+  border-radius: 2px;
+}
+
+.bar-color {
+  background: linear-gradient(135deg, #409EFF, #66b1ff);
+}
+
+.line-color {
+  background: linear-gradient(135deg, #67C23A, #85ce61);
+}
+
+.chart-container {
+  width: 40vw;
+  height: 320px;
+  position: relative;
+}
+
+.detail-card,
+.abnormal-card {
+  border-radius: 12px;
+  border: none;
+  background: #fff;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  margin-bottom: 24px;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 0;
+}
+
+.card-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.stats-table {
+  border-radius: 8px;
+  overflow: hidden;
+}
+
+.stats-table :deep(.el-table__row) {
+  transition: background-color 0.3s;
+}
+
+.stats-table :deep(.el-table__row:hover) {
+  background-color: #f5f7fa;
+}
+
+.stats-table :deep(.even-row) {
+  background-color: #fafbfc;
+}
+
+.progress-cell {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.rate-progress {
+  flex: 1;
+}
+
+.rate-text {
+  font-size: 12px;
+  font-weight: 600;
+  min-width: 40px;
+}
+
+.rate-excellent { color: #67C23A; }
+.rate-good { color: #E6A23C; }
+.rate-average { color: #F56C6C; }
+.rate-poor { color: #909399; }
+
+.abnormal-table {
+  border-radius: 8px;
+  overflow: hidden;
 }
 
 /* 鍝嶅簲寮忚璁� */
 @media (max-width: 1200px) {
-  .stats-overview .el-col {
-    margin-bottom: 16px;
+  .chart-container {
+    height: 280px;
   }
 }
 
 @media (max-width: 768px) {
   .personal-attendance-report {
-    padding: 12px;
+    padding: 16px;
   }
 
   .report-header {
     flex-direction: column;
     align-items: flex-start;
-    gap: 12px;
+    gap: 16px;
   }
 
   .header-actions {
@@ -635,14 +969,42 @@
   }
 
   .stat-content {
-    padding: 12px;
+    padding: 16px;
+    flex-direction: column;
+    text-align: center;
   }
 
   .stat-icon {
-    width: 50px;
-    height: 50px;
-    font-size: 20px;
-    margin-right: 12px;
+    margin-right: 0;
+    margin-bottom: 12px;
+    width: 56px;
+    height: 56px;
+    font-size: 24px;
+  }
+
+  .stat-value {
+    font-size: 28px;
+  }
+
+  .chart-container {
+    height: 240px;
+  }
+
+  .chart-header {
+    flex-direction: column;
+    gap: 12px;
+    align-items: flex-start;
+  }
+
+  .chart-legend {
+    align-self: stretch;
+    justify-content: space-around;
+  }
+}
+
+@media (max-width: 480px) {
+  .stat-col {
+    margin-bottom: 16px;
   }
 
   .stat-value {
@@ -650,7 +1012,7 @@
   }
 
   .chart-container {
-    height: 250px;
+    height: 200px;
   }
 }
 
@@ -662,21 +1024,23 @@
   opacity: 0;
 }
 
-/* 琛ㄦ牸鏍峰紡浼樺寲 */
-.el-table {
-  border-radius: 4px;
-  overflow: hidden;
+/* 婊氬姩鏉℃牱寮忎紭鍖� */
+:deep(::-webkit-scrollbar) {
+  width: 6px;
+  height: 6px;
 }
 
-.el-table::before {
-  display: none;
+:deep(::-webkit-scrollbar-track) {
+  background: #f1f1f1;
+  border-radius: 3px;
 }
 
-/* 鍗$墖鏍囬鏍峰紡 */
-.el-card__header {
-  background: #f8f9fa;
-  border-bottom: 1px solid #ebeef5;
-  font-weight: 600;
-  color: #303133;
+:deep(::-webkit-scrollbar-thumb) {
+  background: #c1c1c1;
+  border-radius: 3px;
+}
+
+:deep(::-webkit-scrollbar-thumb:hover) {
+  background: #a8a8a8;
 }
 </style>
diff --git a/src/views/OfficeRelated/checkingIn/mockData.js b/src/views/OfficeRelated/checkingIn/mockData.js
index f419c38..8c15455 100644
--- a/src/views/OfficeRelated/checkingIn/mockData.js
+++ b/src/views/OfficeRelated/checkingIn/mockData.js
@@ -1,131 +1,52 @@
-// 鍦ㄧ埗缁勪欢 data() 涓垨鍗曠嫭鍒涘缓 mockData.js 鏂囦欢
 export const generateMockData = () => {
   return {
-    // 鍛樺伐鑰冨嫟鏁版嵁
+    // 鍛樺伐鑰冨嫟鏁版嵁 - 闇�瑕佽ˉ鍏呭畬鏁存湀浠界殑鏁版嵁
     attendanceData: [
-      {
-        id: 1,
-        date: '2024-12-01',
-        checkIn: '08:30',
-        checkOut: '18:00',
-        status: 'present',
-        workHours: 9.5
-      },
-      {
-        id: 2,
-        date: '2024-12-02',
-        checkIn: '09:15',
-        checkOut: '18:00',
-        status: 'late',
-        workHours: 8.75
-      },
-      {
-        id: 3,
-        date: '2024-12-03',
-        checkIn: '08:45',
-        checkOut: '17:30',
-        status: 'present',
-        workHours: 8.75
-      },
-      {
-        id: 4,
-        date: '2024-12-04',
-        checkIn: '08:25',
-        checkOut: '18:10',
-        status: 'present',
-        workHours: 9.75
-      },
-      {
-        id: 5,
-        date: '2024-12-05',
-        checkIn: null,
-        checkOut: null,
-        status: 'absent',
-        workHours: 0
-      },
-      {
-        id: 6,
-        date: '2024-12-08',
-        checkIn: '08:40',
-        checkOut: '17:45',
-        status: 'present',
-        workHours: 9.0
-      },
-      {
-        id: 7,
-        date: '2024-12-09',
-        checkIn: '08:35',
-        checkOut: '18:05',
-        status: 'present',
-        workHours: 9.5
-      },
-      {
-        id: 8,
-        date: '2024-12-10',
-        checkIn: '09:05',
-        checkOut: '17:50',
-        status: 'late',
-        workHours: 8.75
-      },
-      {
-        id: 9,
-        date: '2024-12-11',
-        checkIn: '08:50',
-        checkOut: '18:15',
-        status: 'present',
-        workHours: 9.5
-      },
-      {
-        id: 10,
-        date: '2024-12-12',
-        checkIn: '08:30',
-        checkOut: '17:40',
-        status: 'present',
-        workHours: 9.0
-      },
-      {
-        id: 11,
-        date: '2024-12-15',
-        checkIn: '08:45',
-        checkOut: '18:00',
-        status: 'present',
-        workHours: 9.25
-      },
-      {
-        id: 12,
-        date: '2024-12-16',
-        checkIn: '08:55',
-        checkOut: '17:55',
-        status: 'present',
-        workHours: 9.0
-      },
-      {
-        id: 13,
-        date: '2024-12-17',
-        checkIn: '08:40',
-        checkOut: '18:10',
-        status: 'present',
-        workHours: 9.5
-      },
-      {
-        id: 14,
-        date: '2024-12-18',
-        checkIn: '09:20',
-        checkOut: '17:30',
-        status: 'late',
-        workHours: 8.0
-      },
-      {
-        id: 15,
-        date: '2024-12-19',
-        checkIn: '08:35',
-        checkOut: '18:05',
-        status: 'present',
-        workHours: 9.5
-      }
+      // 12鏈�1-4鏃�
+      { id: 1, date: '2024-12-01', checkIn: '08:30', checkOut: '18:00', status: 'present', workHours: 9.5 },
+      { id: 2, date: '2024-12-02', checkIn: '09:15', checkOut: '18:00', status: 'late', workHours: 8.75 },
+      { id: 3, date: '2024-12-03', checkIn: '08:45', checkOut: '17:30', status: 'present', workHours: 8.75 },
+      { id: 4, date: '2024-12-04', checkIn: '08:25', checkOut: '18:10', status: 'present', workHours: 9.75 },
+
+      // 12鏈�5-8鏃ワ紙5鍙风己鍕わ紝5-8鍙峰嚭宸級
+      { id: 5, date: '2024-12-05', checkIn: null, checkOut: null, status: 'absent', workHours: 0 },
+      { id: 6, date: '2024-12-06', checkIn: null, checkOut: null, status: 'trip', workHours: 0 },
+      { id: 7, date: '2024-12-07', checkIn: null, checkOut: null, status: 'trip', workHours: 0 },
+      { id: 8, date: '2024-12-08', checkIn: '08:40', checkOut: '17:45', status: 'present', workHours: 9.0 },
+
+      // 12鏈�9-14鏃�
+      { id: 9, date: '2024-12-09', checkIn: '08:35', checkOut: '18:05', status: 'present', workHours: 9.5 },
+      { id: 10, date: '2024-12-10', checkIn: '09:05', checkOut: '17:50', status: 'late', workHours: 8.75 },
+      { id: 11, date: '2024-12-11', checkIn: '08:50', checkOut: '18:15', status: 'present', workHours: 9.5 },
+      { id: 12, date: '2024-12-12', checkIn: '08:30', checkOut: '17:40', status: 'present', workHours: 9.0 },
+      { id: 13, date: '2024-12-13', checkIn: '08:55', checkOut: '18:00', status: 'present', workHours: 9.0 },
+      { id: 14, date: '2024-12-14', checkIn: '08:45', checkOut: '17:50', status: 'present', workHours: 9.0 },
+
+      // 12鏈�15-18鏃ワ紙鍑哄樊锛�
+      { id: 15, date: '2024-12-15', checkIn: null, checkOut: null, status: 'trip', workHours: 0 },
+      { id: 16, date: '2024-12-16', checkIn: null, checkOut: null, status: 'trip', workHours: 0 },
+      { id: 17, date: '2024-12-17', checkIn: null, checkOut: null, status: 'trip', workHours: 0 },
+      { id: 18, date: '2024-12-18', checkIn: null, checkOut: null, status: 'trip', workHours: 0 },
+
+      // 12鏈�19-24鏃�
+      { id: 19, date: '2024-12-19', checkIn: '08:35', checkOut: '18:05', status: 'present', workHours: 9.5 },
+      { id: 20, date: '2024-12-20', checkIn: '08:40', checkOut: '17:55', status: 'present', workHours: 9.25 },
+      { id: 21, date: '2024-12-21', checkIn: null, checkOut: null, status: 'absent', workHours: 0 }, // 鍛ㄥ叚
+      { id: 22, date: '2024-12-22', checkIn: null, checkOut: null, status: 'trip', workHours: 0 }, // 鍑哄樊
+      { id: 23, date: '2024-12-23', checkIn: null, checkOut: null, status: 'trip', workHours: 0 }, // 鍑哄樊
+      { id: 24, date: '2024-12-24', checkIn: null, checkOut: null, status: 'trip', workHours: 0 }, // 鍑哄樊
+
+      // 12鏈�25-31鏃�
+      { id: 25, date: '2024-12-25', checkIn: '08:50', checkOut: '17:45', status: 'present', workHours: 8.75 },
+      { id: 26, date: '2024-12-26', checkIn: '09:10', checkOut: '17:40', status: 'late', workHours: 8.5 },
+      { id: 27, date: '2024-12-27', checkIn: '08:35', checkOut: '18:00', status: 'present', workHours: 9.25 },
+      { id: 28, date: '2024-12-28', checkIn: '08:45', checkOut: '17:50', status: 'present', workHours: 9.0 },
+      { id: 29, date: '2024-12-29', checkIn: null, checkOut: null, status: 'absent', workHours: 0 }, // 鍛ㄦ棩
+      { id: 30, date: '2024-12-30', checkIn: '08:30', checkOut: '17:30', status: 'present', workHours: 9.0 },
+      { id: 31, date: '2024-12-31', checkIn: '08:40', checkOut: '16:00', status: 'present', workHours: 7.5 }
     ],
 
-    // 鍑哄樊鏁版嵁
+    // 鍑哄樊鏁版嵁 - 闇�瑕佽皟鏁存棩鏈熻寖鍥村尮閰�
     businessTripData: [
       {
         id: 1,
@@ -133,7 +54,7 @@
         startCity: '鍖椾含',
         endCity: '涓婃捣',
         startDate: '2024-12-05',
-        endDate: '2024-12-08',
+        endDate: '2024-12-07', // 璋冩暣涓�7鍙风粨鏉�
         distance: 1200,
         purpose: '瀹㈡埛浼氳',
         status: 'completed'
diff --git a/src/views/OfficeRelated/conference/index.vue b/src/views/OfficeRelated/conference/index.vue
new file mode 100644
index 0000000..5b87c95
--- /dev/null
+++ b/src/views/OfficeRelated/conference/index.vue
@@ -0,0 +1,728 @@
+<template>
+  <div class="meeting-management">
+    <!-- 椤甸潰澶撮儴 -->
+    <div class="page-header">
+      <h2>浼氳绠$悊</h2>
+      <div class="header-actions">
+        <el-button type="primary" icon="el-icon-plus" @click="handleAdd">
+          鏂板缓浼氳
+        </el-button>
+        <el-button icon="el-icon-download" @click="exportData">
+          瀵煎嚭鏁版嵁
+        </el-button>
+      </div>
+    </div>
+
+    <!-- 鎼滅储绛涢�夊尯鍩� -->
+    <el-card class="filter-card">
+      <el-form :model="queryParams" inline>
+        <el-form-item label="浼氳绫诲瀷">
+          <el-select v-model="queryParams.meetingType" clearable placeholder="璇烽�夋嫨">
+            <el-option label="绉戠爺浼氳" value="research" />
+            <el-option label="鏃ュ父浼氳" value="daily" />
+            <el-option label="椤圭洰浼氳" value="project" />
+            <el-option label="閮ㄩ棬浼氳" value="department" />
+            <el-option label="璇勫浼氳" value="review" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="浼氳鍦扮偣">
+          <el-input
+            v-model="queryParams.location"
+            placeholder="璇疯緭鍏ヤ細璁湴鐐�"
+            clearable
+            style="width: 150px"
+          />
+        </el-form-item>
+        <el-form-item label="鏃堕棿鑼冨洿">
+          <el-date-picker
+            v-model="queryParams.dateRange"
+            type="daterange"
+            range-separator="鑷�"
+            start-placeholder="寮�濮嬫棩鏈�"
+            end-placeholder="缁撴潫鏃ユ湡"
+            value-format="yyyy-MM-dd"
+          />
+        </el-form-item>
+        <el-form-item label="鐘舵��">
+          <el-select v-model="queryParams.status" clearable placeholder="璇烽�夋嫨">
+            <el-option label="寰呭紑濮�" value="pending" />
+            <el-option label="杩涜涓�" value="ongoing" />
+            <el-option label="宸茬粨鏉�" value="completed" />
+            <el-option label="宸插彇娑�" value="cancelled" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
+          <el-button @click="handleReset">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- 鏁版嵁琛ㄦ牸 -->
+    <el-card>
+      <el-table
+        :data="tableData"
+        v-loading="loading"
+        border
+        style="width: 100%"
+        @sort-change="handleSortChange"
+      >
+        <el-table-column prop="id" label="ID" width="80" fixed />
+        <el-table-column prop="title" label="浼氳涓婚" width="200" fixed>
+          <template #default="scope">
+            <el-button type="text" @click="handleView(scope.row)">
+              {{ scope.row.title }}
+            </el-button>
+          </template>
+        </el-table-column>
+        <el-table-column prop="meetingType" label="浼氳绫诲瀷" width="120">
+          <template #default="scope">
+            <el-tag :type="getMeetingTypeTag(scope.row.meetingType)">
+              {{ getMeetingTypeText(scope.row.meetingType) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="participants" label="鍙備細浜哄憳" width="180" show-overflow-tooltip>
+          <template #default="scope">
+            <span>{{ scope.row.participants.join(', ') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="location" label="浼氳鍦扮偣" width="150" />
+        <el-table-column prop="startTime" label="寮�濮嬫椂闂�" width="160" sortable>
+          <template #default="scope">
+            <span>{{ formatDateTime(scope.row.startTime) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="endTime" label="缁撴潫鏃堕棿" width="160" sortable>
+          <template #default="scope">
+            <span>{{ formatDateTime(scope.row.endTime) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="duration" label="鎸佺画鏃堕棿" width="100">
+          <template #default="scope">
+            <span>{{ calculateDuration(scope.row) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="summary" label="浼氳姒傝" min-width="200" show-overflow-tooltip />
+        <el-table-column prop="status" label="鐘舵��" width="100" fixed="right">
+          <template #default="scope">
+            <el-tag :type="getStatusTag(scope.row.status)">
+              {{ getStatusText(scope.row.status) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="200" fixed="right">
+          <template #default="scope">
+            <el-button size="mini" type="text" @click="handleView(scope.row)">
+              鏌ョ湅
+            </el-button>
+            <el-button size="mini" type="text" @click="handleEdit(scope.row)">
+              缂栬緫
+            </el-button>
+            <el-button
+              size="mini"
+              type="text"
+              @click="handleCopy(scope.row)"
+              style="color: #67C23A;"
+            >
+              澶嶅埗
+            </el-button>
+            <el-button
+              size="mini"
+              type="text"
+              @click="handleDelete(scope.row)"
+              style="color: #F56C6C;"
+            >
+              鍒犻櫎
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 鍒嗛〉 -->
+      <div class="pagination-container">
+        <el-pagination
+          :current-page="pagination.currentPage"
+          :page-size="pagination.pageSize"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+
+    <!-- 鏌ョ湅璇︽儏瀵硅瘽妗� -->
+    <el-dialog
+      :title="`浼氳璇︽儏 - ${currentRecord.title || ''}`"
+      :visible.sync="detailDialogVisible"
+      width="800px"
+      :before-close="handleDetailClose"
+    >
+      <el-descriptions :column="2" border v-if="currentRecord">
+        <el-descriptions-item label="浼氳涓婚">{{ currentRecord.title }}</el-descriptions-item>
+        <el-descriptions-item label="浼氳绫诲瀷">
+          <el-tag :type="getMeetingTypeTag(currentRecord.meetingType)">
+            {{ getMeetingTypeText(currentRecord.meetingType) }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="鍙備細浜哄憳" :span="2">
+          <el-tag
+            v-for="participant in currentRecord.participants"
+            :key="participant"
+            type="info"
+            size="small"
+            style="margin-right: 8px; margin-bottom: 8px;"
+          >
+            {{ participant }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="浼氳鍦扮偣">{{ currentRecord.location }}</el-descriptions-item>
+        <el-descriptions-item label="浼氳鐘舵��">
+          <el-tag :type="getStatusTag(currentRecord.status)">
+            {{ getStatusText(currentRecord.status) }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="寮�濮嬫椂闂�">{{ formatDateTime(currentRecord.startTime) }}</el-descriptions-item>
+        <el-descriptions-item label="缁撴潫鏃堕棿">{{ formatDateTime(currentRecord.endTime) }}</el-descriptions-item>
+        <el-descriptions-item label="鎸佺画鏃堕棿">{{ calculateDuration(currentRecord) }}</el-descriptions-item>
+        <el-descriptions-item label="鍒涘缓浜�">{{ currentRecord.creator }}</el-descriptions-item>
+        <el-descriptions-item label="浼氳姒傝" :span="2">
+          {{ currentRecord.summary }}
+        </el-descriptions-item>
+        <el-descriptions-item label="浼氳鍐呭" :span="2">
+          <div style="white-space: pre-line; max-height: 300px; overflow-y: auto;">
+            {{ currentRecord.content }}
+          </div>
+        </el-descriptions-item>
+        <el-descriptions-item label="闄勪欢" :span="2" v-if="currentRecord.attachments && currentRecord.attachments.length">
+          <div v-for="file in currentRecord.attachments" :key="file.id" class="attachment-item">
+            <el-link type="primary" :href="file.url" target="_blank">
+              <i class="el-icon-document"></i> {{ file.name }}
+            </el-link>
+          </div>
+        </el-descriptions-item>
+      </el-descriptions>
+      <span slot="footer">
+        <el-button @click="detailDialogVisible = false">鍏抽棴</el-button>
+        <el-button type="primary" @click="handleEdit(currentRecord)">缂栬緫</el-button>
+      </span>
+    </el-dialog>
+
+    <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog
+      :title="`${isEditing ? '缂栬緫' : '鏂板'}浼氳`"
+      :visible.sync="editDialogVisible"
+      width="700px"
+      :before-close="handleEditClose"
+    >
+      <el-form
+        ref="editForm"
+        :model="editForm"
+        :rules="editRules"
+        label-width="100px"
+        label-position="left"
+      >
+        <el-form-item label="浼氳涓婚" prop="title">
+          <el-input v-model="editForm.title" placeholder="璇疯緭鍏ヤ細璁富棰�" />
+        </el-form-item>
+
+        <el-form-item label="浼氳绫诲瀷" prop="meetingType">
+          <el-select v-model="editForm.meetingType" placeholder="璇烽�夋嫨浼氳绫诲瀷" style="width: 100%">
+            <el-option label="绉戠爺浼氳" value="research" />
+            <el-option label="鏃ュ父浼氳" value="daily" />
+            <el-option label="椤圭洰浼氳" value="project" />
+            <el-option label="閮ㄩ棬浼氳" value="department" />
+            <el-option label="璇勫浼氳" value="review" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="鍙備細浜哄憳" prop="participants">
+          <el-select
+            v-model="editForm.participants"
+            multiple
+            filterable
+            placeholder="璇烽�夋嫨鍙備細浜哄憳"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="user in userList"
+              :key="user.id"
+              :label="user.name"
+              :value="user.name"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="浼氳鍦扮偣" prop="location">
+          <el-input v-model="editForm.location" placeholder="璇疯緭鍏ヤ細璁湴鐐�" />
+        </el-form-item>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="寮�濮嬫椂闂�" prop="startTime">
+              <el-date-picker
+                v-model="editForm.startTime"
+                type="datetime"
+                placeholder="閫夋嫨寮�濮嬫椂闂�"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="缁撴潫鏃堕棿" prop="endTime">
+              <el-date-picker
+                v-model="editForm.endTime"
+                type="datetime"
+                placeholder="閫夋嫨缁撴潫鏃堕棿"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="浼氳姒傝" prop="summary">
+          <el-input
+            v-model="editForm.summary"
+            type="textarea"
+            :rows="2"
+            placeholder="璇疯緭鍏ヤ細璁瑕�"
+            maxlength="200"
+            show-word-limit
+          />
+        </el-form-item>
+
+        <el-form-item label="浼氳鍐呭" prop="content">
+          <el-input
+            v-model="editForm.content"
+            type="textarea"
+            :rows="6"
+            placeholder="璇疯緭鍏ヤ細璁叿浣撳唴瀹�"
+          />
+        </el-form-item>
+
+        <el-form-item label="闄勪欢涓婁紶">
+          <el-upload
+            action="#"
+            :auto-upload="false"
+            :on-change="handleFileChange"
+            :file-list="editForm.attachments"
+            :limit="5"
+            multiple
+          >
+            <el-button size="small" type="primary">鐐瑰嚮涓婁紶</el-button>
+            <div slot="tip" class="el-upload__tip">鏀寔涓婁紶鏂囨。銆佸浘鐗囩瓑鏂囦欢锛屽崟涓枃浠朵笉瓒呰繃10MB</div>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+
+      <span slot="footer">
+        <el-button @click="handleEditClose">鍙栨秷</el-button>
+        <el-button type="primary" @click="handleSave" :loading="saveLoading">
+          {{ isEditing ? '淇濆瓨' : '鏂板' }}
+        </el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'MeetingManagement',
+  data() {
+    return {
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        meetingType: '',
+        location: '',
+        dateRange: [],
+        status: ''
+      },
+      // 鍒嗛〉鍙傛暟
+      pagination: {
+        currentPage: 1,
+        pageSize: 10,
+        total: 0
+      },
+      // 鍔犺浇鐘舵��
+      loading: false,
+      saveLoading: false,
+      // 瀵硅瘽妗嗘樉绀虹姸鎬�
+      detailDialogVisible: false,
+      editDialogVisible: false,
+      // 褰撳墠鎿嶄綔璁板綍
+      currentRecord: {},
+      // 缂栬緫鐘舵��
+      isEditing: false,
+      // 琛ㄦ牸鏁版嵁
+      tableData: [],
+      // 鐢ㄦ埛鍒楄〃锛堢敤浜庨�夋嫨鍙備細浜哄憳锛�
+      userList: [
+        { id: 1, name: '寮犱笁' },
+        { id: 2, name: '鏉庡洓' },
+        { id: 3, name: '鐜嬩簲' },
+        { id: 4, name: '璧靛叚' },
+        { id: 5, name: '閽变竷' },
+        { id: 6, name: '瀛欏叓' },
+        { id: 7, name: '鍛ㄤ節' },
+        { id: 8, name: '鍚村崄' }
+      ],
+      // 缂栬緫琛ㄥ崟鏁版嵁
+      editForm: {
+        title: '',
+        meetingType: '',
+        participants: [],
+        location: '',
+        startTime: '',
+        endTime: '',
+        summary: '',
+        content: '',
+        attachments: []
+      },
+      // 琛ㄥ崟楠岃瘉瑙勫垯
+      editRules: {
+        title: [{ required: true, message: '璇疯緭鍏ヤ細璁富棰�', trigger: 'blur' }],
+        meetingType: [{ required: true, message: '璇烽�夋嫨浼氳绫诲瀷', trigger: 'change' }],
+        participants: [{ required: true, message: '璇烽�夋嫨鍙備細浜哄憳', trigger: 'change' }],
+        location: [{ required: true, message: '璇疯緭鍏ヤ細璁湴鐐�', trigger: 'blur' }],
+        startTime: [{ required: true, message: '璇烽�夋嫨寮�濮嬫椂闂�', trigger: 'change' }],
+        endTime: [{ required: true, message: '璇烽�夋嫨缁撴潫鏃堕棿', trigger: 'change' }],
+        summary: [{ required: true, message: '璇疯緭鍏ヤ細璁瑕�', trigger: 'blur' }],
+        content: [{ required: true, message: '璇疯緭鍏ヤ細璁唴瀹�', trigger: 'blur' }]
+      }
+    }
+  },
+  mounted() {
+    this.loadData()
+  },
+  methods: {
+    // 鍔犺浇鏁版嵁
+    async loadData() {
+      this.loading = true
+      try {
+        // 妯℃嫙API璋冪敤
+        await new Promise(resolve => setTimeout(resolve, 500))
+
+        // 鐢熸垚妯℃嫙鏁版嵁
+        this.tableData = this.generateMockData()
+        this.pagination.total = this.tableData.length
+      } catch (error) {
+        console.error('鍔犺浇鏁版嵁澶辫触:', error)
+        this.$message.error('鏁版嵁鍔犺浇澶辫触')
+      } finally {
+        this.loading = false
+      }
+    },
+
+    // 鐢熸垚妯℃嫙鏁版嵁
+    generateMockData() {
+      const meetingTypes = ['research', 'daily', 'project', 'department', 'review']
+      const statuses = ['pending', 'ongoing', 'completed', 'cancelled']
+      const participantsPool = ['寮犱笁', '鏉庡洓', '鐜嬩簲', '璧靛叚', '閽变竷', '瀛欏叓', '鍛ㄤ節', '鍚村崄']
+
+      return Array.from({ length: 10 }, (_, index) => {
+        const participantCount = Math.floor(Math.random() * 5) + 2
+        const participants = []
+        for (let i = 0; i < participantCount; i++) {
+          const randomIndex = Math.floor(Math.random() * participantsPool.length)
+          participants.push(participantsPool[randomIndex])
+        }
+
+        // 鍘婚噸
+        const uniqueParticipants = [...new Set(participants)]
+
+        const startTime = new Date()
+        startTime.setDate(startTime.getDate() + Math.floor(Math.random() * 30) - 15)
+        startTime.setHours(9 + Math.floor(Math.random() * 8), Math.floor(Math.random() * 4) * 15, 0)
+
+        const endTime = new Date(startTime)
+        endTime.setHours(startTime.getHours() + Math.floor(Math.random() * 3) + 1)
+
+        return {
+          id: index + 1,
+          title: `鍏充簬${['绉戠爺椤圭洰', '鏃ュ父宸ヤ綔', '鎶�鏈瘎瀹�', '閮ㄩ棬鍗忚皟'][Math.floor(Math.random() * 4)]}鐨勪細璁甡,
+          meetingType: meetingTypes[Math.floor(Math.random() * meetingTypes.length)],
+          participants: uniqueParticipants,
+          location: ['绗竴浼氳瀹�', '绗簩浼氳瀹�', '绗笁浼氳瀹�', '绾夸笂浼氳'][Math.floor(Math.random() * 4)],
+          startTime: startTime.toISOString(),
+          endTime: endTime.toISOString(),
+          summary: `鏈浼氳涓昏璁ㄨ${['椤圭洰杩涘睍', '鎶�鏈毦棰�', '宸ヤ綔璁″垝', '闂鍗忚皟'][Math.floor(Math.random() * 4)]}绛夌浉鍏充簨瀹渀,
+          content: `浼氳璇︾粏鍐呭锛歕n1. 璁涓�璁ㄨ\n2. 璁浜屽垎鏋怽n3. 涓嬩竴姝ュ伐浣滆鍒抃n4. 浠诲姟鍒嗛厤`,
+          status: statuses[Math.floor(Math.random() * statuses.length)],
+          creator: '绯荤粺绠$悊鍛�',
+          attachments: []
+        }
+      })
+    },
+
+    // 鑾峰彇浼氳绫诲瀷鏍囩鏍峰紡
+    getMeetingTypeTag(type) {
+      const typeMap = {
+        research: 'primary',
+        daily: 'success',
+        project: 'warning',
+        department: 'info',
+        review: 'danger'
+      }
+      return typeMap[type] || 'info'
+    },
+
+    // 鑾峰彇浼氳绫诲瀷鏂囨湰
+    getMeetingTypeText(type) {
+      const textMap = {
+        research: '绉戠爺浼氳',
+        daily: '鏃ュ父浼氳',
+        project: '椤圭洰浼氳',
+        department: '閮ㄩ棬浼氳',
+        review: '璇勫浼氳'
+      }
+      return textMap[type] || type
+    },
+
+    // 鑾峰彇鐘舵�佹爣绛炬牱寮�
+    getStatusTag(status) {
+      const statusMap = {
+        pending: 'primary',
+        ongoing: 'success',
+        completed: 'info',
+        cancelled: 'danger'
+      }
+      return statusMap[status] || 'info'
+    },
+
+    // 鑾峰彇鐘舵�佹枃鏈�
+    getStatusText(status) {
+      const textMap = {
+        pending: '寰呭紑濮�',
+        ongoing: '杩涜涓�',
+        completed: '宸茬粨鏉�',
+        cancelled: '宸插彇娑�'
+      }
+      return textMap[status] || status
+    },
+
+    // 鏍煎紡鍖栨棩鏈熸椂闂�
+    formatDateTime(dateTime) {
+      if (!dateTime) return ''
+      const date = new Date(dateTime)
+      return date.toLocaleString('zh-CN')
+    },
+
+    // 璁$畻浼氳鎸佺画鏃堕棿
+    calculateDuration(record) {
+      if (!record.startTime || !record.endTime) return ''
+      const start = new Date(record.startTime)
+      const end = new Date(record.endTime)
+      const duration = (end - start) / (1000 * 60) // 鍒嗛挓鏁�
+
+      if (duration < 60) {
+        return `${Math.round(duration)}鍒嗛挓`
+      } else {
+        const hours = Math.floor(duration / 60)
+        const minutes = Math.round(duration % 60)
+        return minutes > 0 ? `${hours}灏忔椂${minutes}鍒嗛挓` : `${hours}灏忔椂`
+      }
+    },
+
+    // 鏌ヨ澶勭悊
+    handleQuery() {
+      this.pagination.currentPage = 1
+      this.loadData()
+    },
+
+    // 閲嶇疆鏌ヨ
+    handleReset() {
+      this.queryParams = {
+        meetingType: '',
+        location: '',
+        dateRange: [],
+        status: ''
+      }
+      this.pagination.currentPage = 1
+      this.loadData()
+    },
+
+    // 鏌ョ湅璇︽儏
+    handleView(record) {
+      this.currentRecord = { ...record }
+      this.detailDialogVisible = true
+    },
+
+    // 鏂板璁板綍
+    handleAdd() {
+      this.isEditing = false
+      this.editForm = this.getDefaultFormData()
+      this.editDialogVisible = true
+      this.$nextTick(() => {
+        this.$refs.editForm && this.$refs.editForm.clearValidate()
+      })
+    },
+
+    // 缂栬緫璁板綍
+    handleEdit(record) {
+      this.isEditing = true
+      this.currentRecord = record
+      this.editForm = { ...record }
+      this.editDialogVisible = true
+      this.detailDialogVisible = false
+      this.$nextTick(() => {
+        this.$refs.editForm && this.$refs.editForm.clearValidate()
+      })
+    },
+
+    // 澶嶅埗璁板綍
+    handleCopy(record) {
+      this.isEditing = false
+      const copiedRecord = { ...record }
+      delete copiedRecord.id
+      copiedRecord.title = copiedRecord.title + '锛堝鍒讹級'
+      this.editForm = copiedRecord
+      this.editDialogVisible = true
+      this.$nextTick(() => {
+        this.$refs.editForm && this.$refs.editForm.clearValidate()
+      })
+    },
+
+    // 鍒犻櫎璁板綍
+    handleDelete(record) {
+      this.$confirm('纭畾瑕佸垹闄よ繖鏉′細璁褰曞悧锛�', '鎻愮ず', {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }).then(() => {
+        // 妯℃嫙鍒犻櫎鎿嶄綔
+        this.tableData = this.tableData.filter(item => item.id !== record.id)
+        this.pagination.total = this.tableData.length
+        this.$message.success('鍒犻櫎鎴愬姛')
+      }).catch(() => {})
+    },
+
+    // 淇濆瓨璁板綍
+    async handleSave() {
+      try {
+        const valid = await this.$refs.editForm.validate()
+        if (!valid) return
+
+        this.saveLoading = true
+        // 妯℃嫙API璋冪敤
+        await new Promise(resolve => setTimeout(resolve, 1000))
+
+        this.$message.success(this.isEditing ? '淇濆瓨鎴愬姛' : '鏂板鎴愬姛')
+        this.editDialogVisible = false
+        this.loadData()
+      } catch (error) {
+        console.error('淇濆瓨澶辫触:', error)
+        this.$message.error('鎿嶄綔澶辫触')
+      } finally {
+        this.saveLoading = false
+      }
+    },
+
+    // 鏂囦欢涓婁紶澶勭悊
+    handleFileChange(file, fileList) {
+      this.editForm.attachments = fileList
+    },
+
+    // 鍏抽棴璇︽儏瀵硅瘽妗�
+    handleDetailClose() {
+      this.detailDialogVisible = false
+      this.currentRecord = {}
+    },
+
+    // 鍏抽棴缂栬緫瀵硅瘽妗�
+    handleEditClose() {
+      this.editDialogVisible = false
+      this.currentRecord = {}
+      this.$nextTick(() => {
+        this.$refs.editForm && this.$refs.editForm.clearValidate()
+      })
+    },
+
+    // 瀵煎嚭鏁版嵁
+    exportData() {
+      this.$message.success('瀵煎嚭鍔熻兘寮�鍙戜腑')
+    },
+
+    // 鍒嗛〉澶у皬鍙樺寲
+    handleSizeChange(size) {
+      this.pagination.pageSize = size
+      this.pagination.currentPage = 1
+      this.loadData()
+    },
+
+    // 褰撳墠椤靛彉鍖�
+    handleCurrentChange(page) {
+      this.pagination.currentPage = page
+      this.loadData()
+    },
+
+    // 鎺掑簭鍙樺寲
+    handleSortChange(sort) {
+      console.log('鎺掑簭鍙樺寲:', sort)
+    },
+
+    // 鑾峰彇榛樿琛ㄥ崟鏁版嵁
+    getDefaultFormData() {
+      return {
+        title: '',
+        meetingType: '',
+        participants: [],
+        location: '',
+        startTime: '',
+        endTime: '',
+        summary: '',
+        content: '',
+        attachments: []
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.meeting-management {
+  padding: 20px;
+}
+
+.page-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.page-header h2 {
+  margin: 0;
+  color: #303133;
+}
+
+.filter-card {
+  margin-bottom: 20px;
+}
+
+.pagination-container {
+  margin-top: 20px;
+  display: flex;
+  justify-content: flex-end;
+}
+
+.attachment-item {
+  margin-bottom: 8px;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 768px) {
+  .page-header {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 10px;
+  }
+
+  .header-actions {
+    width: 100%;
+    justify-content: space-between;
+  }
+}
+</style>
diff --git a/src/views/OfficeRelated/engage/index.vue b/src/views/OfficeRelated/engage/index.vue
new file mode 100644
index 0000000..21c2dd2
--- /dev/null
+++ b/src/views/OfficeRelated/engage/index.vue
@@ -0,0 +1,691 @@
+<template>
+  <div class="training-management">
+    <!-- 椤甸潰澶撮儴 -->
+    <div class="page-header">
+      <h2>杩涗慨绠$悊</h2>
+      <div class="header-actions">
+        <el-button type="primary" icon="el-icon-plus" @click="handleAdd">
+          鏂板杩涗慨璁板綍
+        </el-button>
+        <el-button icon="el-icon-download" @click="exportData">
+          瀵煎嚭鏁版嵁
+        </el-button>
+      </div>
+    </div>
+
+    <!-- 鎼滅储绛涢�夊尯鍩� -->
+    <el-card class="filter-card">
+      <el-form :model="queryParams" inline>
+        <el-form-item label="濮撳悕">
+          <el-input
+            v-model="queryParams.name"
+            placeholder="璇疯緭鍏ュ鍚�"
+            clearable
+            style="width: 120px"
+          />
+        </el-form-item>
+        <el-form-item label="杩涗慨绫诲瀷">
+          <el-select v-model="queryParams.trainingType" clearable placeholder="璇烽�夋嫨">
+            <el-option label="涓撲笟鎶�鏈繘淇�" value="professional" />
+            <el-option label="绠$悊鑳藉姏杩涗慨" value="management" />
+            <el-option label="瀛﹀巻鎻愬崌杩涗慨" value="education" />
+            <el-option label="鎶�鑳藉煿璁�" value="skill" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鎶�鏈亴绉�">
+          <el-input
+            v-model="queryParams.technicalTitle"
+            placeholder="璇疯緭鍏ユ妧鏈亴绉�"
+            clearable
+            style="width: 140px"
+          />
+        </el-form-item>
+        <el-form-item label="杩涗慨鏃堕棿">
+          <el-date-picker
+            v-model="queryParams.dateRange"
+            type="daterange"
+            range-separator="鑷�"
+            start-placeholder="寮�濮嬫棩鏈�"
+            end-placeholder="缁撴潫鏃ユ湡"
+            value-format="yyyy-MM-dd"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
+          <el-button @click="handleReset">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- 鏁版嵁琛ㄦ牸 -->
+    <el-card>
+      <el-table
+        :data="tableData"
+        v-loading="loading"
+        border
+        style="width: 100%"
+        @sort-change="handleSortChange"
+      >
+        <el-table-column prop="id" label="ID" width="80" fixed />
+        <el-table-column prop="name" label="濮撳悕" width="100" fixed>
+          <template #default="scope">
+            <el-button type="text" @click="handleView(scope.row)">
+              {{ scope.row.name }}
+            </el-button>
+          </template>
+        </el-table-column>
+        <el-table-column prop="gender" label="鎬у埆" width="80">
+          <template #default="scope">
+            <el-tag :type="scope.row.gender === '鐢�' ? 'primary' : 'danger'" size="small">
+              {{ scope.row.gender }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="age" label="骞撮緞" width="80" sortable />
+        <el-table-column prop="education" label="瀛﹀巻" width="100" />
+        <el-table-column prop="trainingType" label="杩涗慨绫诲瀷" width="120">
+          <template #default="scope">
+            <el-tag :type="getTrainingTypeTag(scope.row.trainingType)">
+              {{ getTrainingTypeText(scope.row.trainingType) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="idCard" label="韬唤璇佸彿" width="180" show-overflow-tooltip />
+        <el-table-column prop="graduateSchool" label="姣曚笟闄㈡牎" width="150" show-overflow-tooltip />
+        <el-table-column prop="workUnit" label="鎵�鍦ㄥ崟浣�" width="150" show-overflow-tooltip />
+        <el-table-column prop="technicalTitle" label="鎶�鏈亴绉�" width="120" />
+        <el-table-column prop="professionalField" label="浠庝簨涓撲笟" width="120" />
+        <el-table-column prop="workYears" label="宸ヤ綔骞撮檺" width="100" sortable />
+        <el-table-column prop="trainingStartDate" label="杩涗慨寮�濮嬫椂闂�" width="120" />
+        <el-table-column prop="trainingEndDate" label="杩涗慨缁撴潫鏃堕棿" width="120" />
+        <el-table-column prop="trainingMajor" label="杩涗慨涓撲笟" width="120" />
+        <el-table-column prop="status" label="鐘舵��" width="100" fixed="right">
+          <template #default="scope">
+            <el-tag :type="getStatusTag(scope.row.status)">
+              {{ scope.row.status }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="180" fixed="right">
+          <template #default="scope">
+            <el-button size="mini" type="text" @click="handleView(scope.row)">
+              鏌ョ湅
+            </el-button>
+            <el-button size="mini" type="text" @click="handleEdit(scope.row)">
+              缂栬緫
+            </el-button>
+            <el-button
+              size="mini"
+              type="text"
+              @click="handleCopy(scope.row)"
+              style="color: #67C23A;"
+            >
+              澶嶅埗
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 鍒嗛〉 -->
+      <div class="pagination-container">
+        <el-pagination
+          :current-page="pagination.currentPage"
+          :page-size="pagination.pageSize"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+
+    <!-- 鏌ョ湅璇︽儏瀵硅瘽妗� -->
+    <el-dialog
+      :title="`杩涗慨璇︽儏 - ${currentRecord.name || ''}`"
+      :visible.sync="detailDialogVisible"
+      width="900px"
+      :before-close="handleDetailClose"
+    >
+      <el-descriptions :column="2" border v-if="currentRecord">
+        <el-descriptions-item label="濮撳悕">{{ currentRecord.name }}</el-descriptions-item>
+        <el-descriptions-item label="鎬у埆">{{ currentRecord.gender }}</el-descriptions-item>
+        <el-descriptions-item label="骞撮緞">{{ currentRecord.age }}</el-descriptions-item>
+        <el-descriptions-item label="瀛﹀巻">{{ currentRecord.education }}</el-descriptions-item>
+        <el-descriptions-item label="杩涗慨绫诲瀷">
+          <el-tag :type="getTrainingTypeTag(currentRecord.trainingType)">
+            {{ getTrainingTypeText(currentRecord.trainingType) }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="韬唤璇佸彿">{{ currentRecord.idCard }}</el-descriptions-item>
+        <el-descriptions-item label="姣曚笟闄㈡牎">{{ currentRecord.graduateSchool }}</el-descriptions-item>
+        <el-descriptions-item label="鎵�鍦ㄥ崟浣�">{{ currentRecord.workUnit }}</el-descriptions-item>
+        <el-descriptions-item label="鎶�鏈亴绉�">{{ currentRecord.technicalTitle }}</el-descriptions-item>
+        <el-descriptions-item label="浠庝簨涓撲笟">{{ currentRecord.professionalField }}</el-descriptions-item>
+        <el-descriptions-item label="宸ヤ綔骞撮檺">{{ currentRecord.workYears }}骞�</el-descriptions-item>
+        <el-descriptions-item label="杩涗慨鐩爣">{{ currentRecord.trainingObjective }}</el-descriptions-item>
+        <el-descriptions-item label="杩涗慨寮�濮嬫椂闂�">{{ currentRecord.trainingStartDate }}</el-descriptions-item>
+        <el-descriptions-item label="杩涗慨缁撴潫鏃堕棿">{{ currentRecord.trainingEndDate }}</el-descriptions-item>
+        <el-descriptions-item label="杩涗慨涓撲笟">{{ currentRecord.trainingMajor }}</el-descriptions-item>
+        <el-descriptions-item label="浠庝簨宸ヤ綔鎯呭喌" :span="2">
+          {{ currentRecord.workSituation }}
+        </el-descriptions-item>
+        <el-descriptions-item label="杩涗慨绉戠洰鍙婄洰鐨�" :span="2">
+          {{ currentRecord.trainingSubject }}
+        </el-descriptions-item>
+        <el-descriptions-item label="涓昏瀛﹀巻" :span="2">
+          <div style="white-space: pre-line;">{{ currentRecord.mainEducation }}</div>
+        </el-descriptions-item>
+        <el-descriptions-item label="涓昏宸ヤ綔缁忓巻" :span="2">
+          <div style="white-space: pre-line;">{{ currentRecord.workExperience }}</div>
+        </el-descriptions-item>
+      </el-descriptions>
+      <span slot="footer">
+        <el-button @click="detailDialogVisible = false">鍏抽棴</el-button>
+        <el-button type="primary" @click="handleEdit(currentRecord)">缂栬緫</el-button>
+      </span>
+    </el-dialog>
+
+    <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog
+      :title="`${isEditing ? '缂栬緫' : '鏂板'}杩涗慨璁板綍`"
+      :visible.sync="editDialogVisible"
+      width="80vw"
+      :before-close="handleEditClose"
+    >
+      <el-form
+        ref="editForm"
+        :model="editForm"
+        :rules="editRules"
+        label-width="120px"
+        label-position="left"
+      >
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="濮撳悕" prop="name">
+              <el-input v-model="editForm.name" placeholder="璇疯緭鍏ュ鍚�" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鎬у埆" prop="gender">
+              <el-radio-group v-model="editForm.gender">
+                <el-radio label="鐢�">鐢�</el-radio>
+                <el-radio label="濂�">濂�</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="骞撮緞" prop="age">
+              <el-input-number
+                v-model="editForm.age"
+                :min="18"
+                :max="65"
+                controls-position="right"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瀛﹀巻" prop="education">
+              <el-select v-model="editForm.education" placeholder="璇烽�夋嫨瀛﹀巻" style="width: 100%">
+                <el-option label="鍗氬+" value="鍗氬+" />
+                <el-option label="纭曞+" value="纭曞+" />
+                <el-option label="鏈" value="鏈" />
+                <el-option label="澶т笓" value="澶т笓" />
+                <el-option label="涓笓" value="涓笓" />
+                <el-option label="楂樹腑" value="楂樹腑" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="杩涗慨绫诲瀷" prop="trainingType">
+          <el-select v-model="editForm.trainingType" placeholder="璇烽�夋嫨杩涗慨绫诲瀷" style="width: 100%">
+            <el-option label="涓撲笟鎶�鏈繘淇�" value="professional" />
+            <el-option label="绠$悊鑳藉姏杩涗慨" value="management" />
+            <el-option label="瀛﹀巻鎻愬崌杩涗慨" value="education" />
+            <el-option label="鎶�鑳藉煿璁�" value="skill" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="韬唤璇佸彿" prop="idCard">
+          <el-input v-model="editForm.idCard" placeholder="璇疯緭鍏ヨ韩浠借瘉鍙�" />
+        </el-form-item>
+
+        <el-form-item label="姣曚笟闄㈡牎" prop="graduateSchool">
+          <el-input v-model="editForm.graduateSchool" placeholder="璇疯緭鍏ユ瘯涓氶櫌鏍�" />
+        </el-form-item>
+
+        <el-form-item label="鎵�鍦ㄥ崟浣�" prop="workUnit">
+          <el-input v-model="editForm.workUnit" placeholder="璇疯緭鍏ユ墍鍦ㄥ崟浣�" />
+        </el-form-item>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鎶�鏈亴绉�" prop="technicalTitle">
+              <el-input v-model="editForm.technicalTitle" placeholder="璇疯緭鍏ユ妧鏈亴绉�" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浠庝簨涓撲笟" prop="professionalField">
+              <el-input v-model="editForm.professionalField" placeholder="璇疯緭鍏ヤ粠浜嬩笓涓�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="宸ヤ綔骞撮檺" prop="workYears">
+          <el-input-number
+            v-model="editForm.workYears"
+            :min="0"
+            :max="50"
+            controls-position="right"
+            style="width: 100%"
+          />
+        </el-form-item>
+
+        <el-form-item label="杩涗慨鐩爣" prop="trainingObjective">
+          <el-input
+            v-model="editForm.trainingObjective"
+            type="textarea"
+            :rows="2"
+            placeholder="璇疯緭鍏ヨ繘淇洰鏍�"
+          />
+        </el-form-item>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="杩涗慨寮�濮嬫椂闂�" prop="trainingStartDate">
+              <el-date-picker
+                v-model="editForm.trainingStartDate"
+                type="date"
+                placeholder="閫夋嫨寮�濮嬫棩鏈�"
+                value-format="yyyy-MM-dd"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="杩涗慨缁撴潫鏃堕棿" prop="trainingEndDate">
+              <el-date-picker
+                v-model="editForm.trainingEndDate"
+                type="date"
+                placeholder="閫夋嫨缁撴潫鏃ユ湡"
+                value-format="yyyy-MM-dd"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="杩涗慨涓撲笟" prop="trainingMajor">
+          <el-input v-model="editForm.trainingMajor" placeholder="璇疯緭鍏ヨ繘淇笓涓�" />
+        </el-form-item>
+
+        <el-form-item label="浠庝簨宸ヤ綔鎯呭喌" prop="workSituation">
+          <el-input
+            v-model="editForm.workSituation"
+            type="textarea"
+            :rows="3"
+            placeholder="璇疯緭鍏ヤ粠浜嬪伐浣滄儏鍐�"
+          />
+        </el-form-item>
+
+        <el-form-item label="杩涗慨绉戠洰鍙婄洰鐨�" prop="trainingSubject">
+          <el-input
+            v-model="editForm.trainingSubject"
+            type="textarea"
+            :rows="3"
+            placeholder="璇疯緭鍏ユ湰娆$敵璇疯繘淇綍绉嶇鐩強鐩殑瑕佹眰"
+          />
+        </el-form-item>
+
+        <el-form-item label="涓昏瀛﹀巻" prop="mainEducation">
+          <el-input
+            v-model="editForm.mainEducation"
+            type="textarea"
+            :rows="3"
+            placeholder="璇疯緭鍏ヤ富瑕佸鍘嗭紙姣忚涓�鏉★級"
+          />
+        </el-form-item>
+
+        <el-form-item label="涓昏宸ヤ綔缁忓巻" prop="workExperience">
+          <el-input
+            v-model="editForm.workExperience"
+            type="textarea"
+            :rows="3"
+            placeholder="璇疯緭鍏ヤ富瑕佸伐浣滅粡鍘嗭紙姣忚涓�鏉★級"
+          />
+        </el-form-item>
+      </el-form>
+
+      <span slot="footer">
+        <el-button @click="handleEditClose">鍙栨秷</el-button>
+        <el-button type="primary" @click="handleSave" :loading="saveLoading">
+          {{ isEditing ? '淇濆瓨' : '鏂板' }}
+        </el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'TrainingManagement',
+  data() {
+    return {
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        name: '',
+        trainingType: '',
+        technicalTitle: '',
+        dateRange: []
+      },
+      // 鍒嗛〉鍙傛暟
+      pagination: {
+        currentPage: 1,
+        pageSize: 10,
+        total: 0
+      },
+      // 鍔犺浇鐘舵��
+      loading: false,
+      saveLoading: false,
+      // 瀵硅瘽妗嗘樉绀虹姸鎬�
+      detailDialogVisible: false,
+      editDialogVisible: false,
+      // 褰撳墠鎿嶄綔璁板綍
+      currentRecord: {},
+      // 缂栬緫鐘舵��
+      isEditing: false,
+      // 琛ㄦ牸鏁版嵁
+      tableData: [],
+      // 缂栬緫琛ㄥ崟鏁版嵁
+      editForm: {
+        name: '',
+        gender: '鐢�',
+        age: 25,
+        education: '',
+        trainingType: '',
+        idCard: '',
+        graduateSchool: '',
+        workUnit: '',
+        technicalTitle: '',
+        professionalField: '',
+        workYears: 0,
+        trainingObjective: '',
+        trainingStartDate: '',
+        trainingEndDate: '',
+        trainingMajor: '',
+        workSituation: '',
+        trainingSubject: '',
+        mainEducation: '',
+        workExperience: ''
+      },
+      // 琛ㄥ崟楠岃瘉瑙勫垯
+      editRules: {
+        name: [{ required: true, message: '璇疯緭鍏ュ鍚�', trigger: 'blur' }],
+        gender: [{ required: true, message: '璇烽�夋嫨鎬у埆', trigger: 'change' }],
+        age: [{ required: true, message: '璇疯緭鍏ュ勾榫�', trigger: 'blur' }],
+        education: [{ required: true, message: '璇烽�夋嫨瀛﹀巻', trigger: 'change' }],
+        trainingType: [{ required: true, message: '璇烽�夋嫨杩涗慨绫诲瀷', trigger: 'change' }],
+        idCard: [{ required: true, message: '璇疯緭鍏ヨ韩浠借瘉鍙�', trigger: 'blur' }]
+      }
+    }
+  },
+  mounted() {
+    this.loadData()
+  },
+  methods: {
+    // 鍔犺浇鏁版嵁
+    async loadData() {
+      this.loading = true
+      try {
+        // 妯℃嫙API璋冪敤
+        await new Promise(resolve => setTimeout(resolve, 500))
+
+        // 鐢熸垚妯℃嫙鏁版嵁
+        this.tableData = this.generateMockData()
+        this.pagination.total = this.tableData.length
+      } catch (error) {
+        console.error('鍔犺浇鏁版嵁澶辫触:', error)
+        this.$message.error('鏁版嵁鍔犺浇澶辫触')
+      } finally {
+        this.loading = false
+      }
+    },
+    // 鐢熸垚妯℃嫙鏁版嵁
+    generateMockData() {
+      const names = ['寮犱笁', '鏉庡洓', '鐜嬩簲', '璧靛叚', '閽变竷', '瀛欏叓', '鍛ㄤ節', '鍚村崄']
+      const trainingTypes = ['professional', 'management', 'education', 'skill']
+      const statuses = ['杩涜涓�', '宸插畬鎴�', '寰呭鏍�', '宸插彇娑�']
+
+      return Array.from({ length: 10 }, (_, index) => ({
+        id: index + 1,
+        name: names[Math.floor(Math.random() * names.length)],
+        gender: Math.random() > 0.5 ? '鐢�' : '濂�',
+        age: 25 + Math.floor(Math.random() * 20),
+        education: ['鍗氬+', '纭曞+', '鏈', '澶т笓'][Math.floor(Math.random() * 4)],
+        trainingType: trainingTypes[Math.floor(Math.random() * trainingTypes.length)],
+        idCard: '11010119900101' + (1000 + index).toString().slice(-4),
+        graduateSchool: ['鍖椾含澶у', '娓呭崕澶у', '澶嶆棪澶у', '涓婃捣浜ら�氬ぇ瀛�'][Math.floor(Math.random() * 4)],
+        workUnit: ['鍖椾含鍗忓拰鍖婚櫌', '涓婃捣鐟為噾鍖婚櫌', '骞垮窞涓北鍖婚櫌', '姝︽眽鍚屾祹鍖婚櫌'][Math.floor(Math.random() * 4)],
+        technicalTitle: ['涓讳换鍖诲笀', '鍓富浠诲尰甯�', '涓绘不鍖诲笀', '浣忛櫌鍖诲笀'][Math.floor(Math.random() * 4)],
+        professionalField: ['蹇冭绠″唴绉�', '绁炵粡澶栫', '鍎跨', '濡囦骇绉�'][Math.floor(Math.random() * 4)],
+        workYears: 5 + Math.floor(Math.random() * 20),
+        trainingObjective: '鎻愬崌涓撲笟鎶�鏈拰涓村簥鑳藉姏',
+        trainingStartDate: '2024-' + (Math.floor(Math.random() * 12) + 1).toString().padStart(2, '0') + '-01',
+        trainingEndDate: '2024-' + (Math.floor(Math.random() * 12) + 1).toString().padStart(2, '0') + '-28',
+        trainingMajor: '涓村簥鍖诲',
+        workSituation: '鍦ㄤ复搴婁竴绾垮伐浣滐紝璐熻矗鎮h�呰瘖鐤�',
+        trainingSubject: '楂樼骇蹇冭绠′粙鍏ユ不鐤楁妧鏈�',
+        mainEducation: '2005-2009 鍖椾含澶у鍖诲閮� 涓村簥鍖诲鏈\n2009-2012 鍖椾含澶у鍖诲閮� 纭曞+',
+        workExperience: '2012-2015 鍖椾含鍗忓拰鍖婚櫌 浣忛櫌鍖诲笀\n2015-2020 鍖椾含鍗忓拰鍖婚櫌 涓绘不鍖诲笀',
+        status: statuses[Math.floor(Math.random() * statuses.length)]
+      }))
+    },
+    // 鑾峰彇杩涗慨绫诲瀷鏍囩鏍峰紡
+    getTrainingTypeTag(type) {
+      const typeMap = {
+        professional: 'primary',
+        management: 'success',
+        education: 'warning',
+        skill: 'info'
+      }
+      return typeMap[type] || 'info'
+    },
+    // 鑾峰彇杩涗慨绫诲瀷鏂囨湰
+    getTrainingTypeText(type) {
+      const textMap = {
+        professional: '涓撲笟鎶�鏈繘淇�',
+        management: '绠$悊鑳藉姏杩涗慨',
+        education: '瀛﹀巻鎻愬崌杩涗慨',
+        skill: '鎶�鑳藉煿璁�'
+      }
+      return textMap[type] || type
+    },
+    // 鑾峰彇鐘舵�佹爣绛炬牱寮�
+    getStatusTag(status) {
+      const statusMap = {
+        '杩涜涓�': 'primary',
+        '宸插畬鎴�': 'success',
+        '寰呭鏍�': 'warning',
+        '宸插彇娑�': 'danger'
+      }
+      return statusMap[status] || 'info'
+    },
+    // 鏌ヨ澶勭悊
+    handleQuery() {
+      this.pagination.currentPage = 1
+      this.loadData()
+    },
+    // 閲嶇疆鏌ヨ
+    handleReset() {
+      this.queryParams = {
+        name: '',
+        trainingType: '',
+        technicalTitle: '',
+        dateRange: []
+      }
+      this.pagination.currentPage = 1
+      this.loadData()
+    },
+    // 鏌ョ湅璇︽儏
+    handleView(record) {
+      this.currentRecord = { ...record }
+      this.detailDialogVisible = true
+    },
+    // 鏂板璁板綍
+    handleAdd() {
+      this.isEditing = false
+      this.editForm = this.getDefaultFormData()
+      this.editDialogVisible = true
+      this.$nextTick(() => {
+        this.$refs.editForm && this.$refs.editForm.clearValidate()
+      })
+    },
+    // 缂栬緫璁板綍
+    handleEdit(record) {
+      this.isEditing = true
+      this.currentRecord = record
+      this.editForm = { ...record }
+      this.editDialogVisible = true
+      this.detailDialogVisible = false
+      this.$nextTick(() => {
+        this.$refs.editForm && this.$refs.editForm.clearValidate()
+      })
+    },
+    // 澶嶅埗璁板綍
+    handleCopy(record) {
+      this.isEditing = false
+      const copiedRecord = { ...record }
+      delete copiedRecord.id
+      copiedRecord.name = copiedRecord.name + '锛堝鍒讹級'
+      this.editForm = copiedRecord
+      this.editDialogVisible = true
+      this.$nextTick(() => {
+        this.$refs.editForm && this.$refs.editForm.clearValidate()
+      })
+    },
+    // 淇濆瓨璁板綍
+    async handleSave() {
+      try {
+        const valid = await this.$refs.editForm.validate()
+        if (!valid) return
+
+        this.saveLoading = true
+        // 妯℃嫙API璋冪敤
+        await new Promise(resolve => setTimeout(resolve, 1000))
+
+        this.$message.success(this.isEditing ? '淇濆瓨鎴愬姛' : '鏂板鎴愬姛')
+        this.editDialogVisible = false
+        this.loadData()
+      } catch (error) {
+        console.error('淇濆瓨澶辫触:', error)
+        this.$message.error('鎿嶄綔澶辫触')
+      } finally {
+        this.saveLoading = false
+      }
+    },
+    // 鍏抽棴璇︽儏瀵硅瘽妗�
+    handleDetailClose() {
+      this.detailDialogVisible = false
+      this.currentRecord = {}
+    },
+    // 鍏抽棴缂栬緫瀵硅瘽妗�
+    handleEditClose() {
+      this.editDialogVisible = false
+      this.currentRecord = {}
+      this.$nextTick(() => {
+        this.$refs.editForm && this.$refs.editForm.clearValidate()
+      })
+    },
+    // 瀵煎嚭鏁版嵁
+    exportData() {
+      this.$message.success('瀵煎嚭鍔熻兘寮�鍙戜腑')
+    },
+    // 鍒嗛〉澶у皬鍙樺寲
+    handleSizeChange(size) {
+      this.pagination.pageSize = size
+      this.pagination.currentPage = 1
+      this.loadData()
+    },
+    // 褰撳墠椤靛彉鍖�
+    handleCurrentChange(page) {
+      this.pagination.currentPage = page
+      this.loadData()
+    },
+    // 鎺掑簭鍙樺寲
+    handleSortChange(sort) {
+      console.log('鎺掑簭鍙樺寲:', sort)
+      // 杩欓噷鍙互娣诲姞鎺掑簭閫昏緫
+    },
+    // 鑾峰彇榛樿琛ㄥ崟鏁版嵁
+    getDefaultFormData() {
+      return {
+        name: '',
+        gender: '鐢�',
+        age: 25,
+        education: '',
+        trainingType: '',
+        idCard: '',
+        graduateSchool: '',
+        workUnit: '',
+        technicalTitle: '',
+        professionalField: '',
+        workYears: 0,
+        trainingObjective: '',
+        trainingStartDate: '',
+        trainingEndDate: '',
+        trainingMajor: '',
+        workSituation: '',
+        trainingSubject: '',
+        mainEducation: '',
+        workExperience: ''
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.training-management {
+  padding: 20px;
+}
+
+.page-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.page-header h2 {
+  margin: 0;
+  color: #303133;
+}
+
+.filter-card {
+  margin-bottom: 20px;
+}
+
+.pagination-container {
+  margin-top: 20px;
+  display: flex;
+  justify-content: flex-end;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 768px) {
+  .page-header {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 10px;
+  }
+
+  .header-actions {
+    width: 100%;
+    justify-content: space-between;
+  }
+}
+</style>

--
Gitblit v1.9.3