From 1c6c29ef2305f8365854a8a5ee03396a3ac69932 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 14 八月 2025 14:17:01 +0800
Subject: [PATCH] 通知公告、会议看板、预警联动机制功能添加

---
 src/views/collaborativeApproval/meetingBoard/index.vue     |  498 +++++++++++++
 src/api/collaborativeApproval/noticeManagement.js          |   69 +
 src/views/collaborativeApproval/warningSystem/index.vue    |  307 ++++++++
 src/views/demo/fakePage/index.vue                          |  248 ++++++
 src/router/index.js                                        |  392 +++++-----
 src/views/collaborativeApproval/noticeManagement/index.vue |  705 +++++++++++++++++++
 6 files changed, 2,017 insertions(+), 202 deletions(-)

diff --git a/src/api/collaborativeApproval/noticeManagement.js b/src/api/collaborativeApproval/noticeManagement.js
new file mode 100644
index 0000000..fa1caec
--- /dev/null
+++ b/src/api/collaborativeApproval/noticeManagement.js
@@ -0,0 +1,69 @@
+import request from '@/utils/request'
+
+// 鏌ヨ鍏憡鍒楄〃
+export function listNotice(query) {
+  return request({
+    url: '/collaborativeApproval/notice/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 鏌ヨ鍏憡璇︾粏
+export function getNotice(noticeId) {
+  return request({
+    url: '/collaborativeApproval/notice/' + noticeId,
+    method: 'get'
+  })
+}
+
+// 鏂板鍏憡
+export function addNotice(data) {
+  return request({
+    url: '/collaborativeApproval/notice',
+    method: 'post',
+    data: data
+  })
+}
+
+// 淇敼鍏憡
+export function updateNotice(data) {
+  return request({
+    url: '/collaborativeApproval/notice',
+    method: 'put',
+    data: data
+  })
+}
+
+// 鍒犻櫎鍏憡
+export function delNotice(noticeId) {
+  return request({
+    url: '/collaborativeApproval/notice/' + noticeId,
+    method: 'delete'
+  })
+}
+
+// 鎵归噺鍒犻櫎鍏憡
+export function delNoticeBatch(noticeIds) {
+  return request({
+    url: '/collaborativeApproval/notice/batch',
+    method: 'delete',
+    data: noticeIds
+  })
+}
+
+// 鍙戝竷鍏憡
+export function publishNotice(noticeId) {
+  return request({
+    url: '/collaborativeApproval/notice/publish/' + noticeId,
+    method: 'put'
+  })
+}
+
+// 涓嬬嚎鍏憡
+export function offlineNotice(noticeId) {
+  return request({
+    url: '/collaborativeApproval/notice/offline/' + noticeId,
+    method: 'put'
+  })
+}
diff --git a/src/router/index.js b/src/router/index.js
index 2ee88bd..f6f0299 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,202 +1,190 @@
-import { createWebHistory, createRouter } from 'vue-router'
-/* Layout */
-import Layout from '@/layout'
-
-/**
- * Note: 璺敱閰嶇疆椤�
- *
- * hidden: true                     // 褰撹缃� true 鐨勬椂鍊欒璺敱涓嶄細鍐嶄晶杈规爮鍑虹幇 濡�401锛宭ogin绛夐〉闈紝鎴栬�呭涓�浜涚紪杈戦〉闈�/edit/1
- * alwaysShow: true                 // 褰撲綘涓�涓矾鐢变笅闈㈢殑 children 澹版槑鐨勮矾鐢卞ぇ浜�1涓椂锛岃嚜鍔ㄤ細鍙樻垚宓屽鐨勬ā寮�--濡傜粍浠堕〉闈�
- *                                  // 鍙湁涓�涓椂锛屼細灏嗛偅涓瓙璺敱褰撳仛鏍硅矾鐢辨樉绀哄湪渚ц竟鏍�--濡傚紩瀵奸〉闈�
- *                                  // 鑻ヤ綘鎯充笉绠¤矾鐢变笅闈㈢殑 children 澹版槑鐨勪釜鏁伴兘鏄剧ず浣犵殑鏍硅矾鐢�
- *                                  // 浣犲彲浠ヨ缃� alwaysShow: true锛岃繖鏍峰畠灏变細蹇界暐涔嬪墠瀹氫箟鐨勮鍒欙紝涓�鐩存樉绀烘牴璺敱
- * redirect: noRedirect             // 褰撹缃� noRedirect 鐨勬椂鍊欒璺敱鍦ㄩ潰鍖呭睉瀵艰埅涓笉鍙鐐瑰嚮
- * name:'router-name'               // 璁惧畾璺敱鐨勫悕瀛楋紝涓�瀹氳濉啓涓嶇劧浣跨敤<keep-alive>鏃朵細鍑虹幇鍚勭闂
- * query: '{"id": 1, "name": "ry"}' // 璁块棶璺敱鐨勯粯璁や紶閫掑弬鏁�
- * roles: ['admin', 'common']       // 璁块棶璺敱鐨勮鑹叉潈闄�
- * permissions: ['a:a:a', 'b:b:b']  // 璁块棶璺敱鐨勮彍鍗曟潈闄�
- * meta : {
-    noCache: true                   // 濡傛灉璁剧疆涓簍rue锛屽垯涓嶄細琚� <keep-alive> 缂撳瓨(榛樿 false)
-    title: 'title'                  // 璁剧疆璇ヨ矾鐢卞湪渚ц竟鏍忓拰闈㈠寘灞戜腑灞曠ず鐨勫悕瀛�
-    icon: 'svg-name'                // 璁剧疆璇ヨ矾鐢辩殑鍥炬爣锛屽搴旇矾寰剆rc/assets/icons/svg
-    breadcrumb: false               // 濡傛灉璁剧疆涓篺alse锛屽垯涓嶄細鍦╞readcrumb闈㈠寘灞戜腑鏄剧ず
-    activeMenu: '/system/user'      // 褰撹矾鐢辫缃簡璇ュ睘鎬э紝鍒欎細楂樹寒鐩稿搴旂殑渚ц竟鏍忋��
-  }
- */
-
-// 鍏叡璺敱
-export const constantRoutes = [
-  {
-    path: '/redirect',
-    component: Layout,
-    hidden: true,
-    children: [
-      {
-        path: '/redirect/:path(.*)',
-        component: () => import('@/views/redirect/index.vue')
-      }
-    ]
-  },
-  {
-    path: '/login',
-    component: () => import('@/views/login'),
-    hidden: true
-  },
-  {
-    path: '/register',
-    component: () => import('@/views/register'),
-    hidden: true
-  },
-  {
-    path: "/:pathMatch(.*)*",
-    component: () => import('@/views/error/404'),
-    hidden: true
-  },
-  {
-    path: '/401',
-    component: () => import('@/views/error/401'),
-    hidden: true
-  },
-
-  {
-    path: '',
-    component: Layout,
-    redirect: '/index',
-    children: [
-      {
-        path: '/index',
-        component: () => import('@/views/index'),
-        name: 'Index',
-        meta: { title: '棣栭〉', icon: 'dashboard', affix: true }
-      }
-    ]
-  },
-  // {
-  //   path: '/equipment',
-  //   component: Layout,
-  //   redirect: '/equipment/iot-monitor',
-  //   children: [
-  //     {
-  //       path: 'iot-monitor',
-  //       component: () => import('@/views/equipmentManagement/iotMonitor/index.vue'),
-  //       name: 'IoTMonitor',
-  //       meta: { title: 'IoT鐩戞帶', icon: 'monitor', noCache: true }
-  //     }
-  //   ]
-  // },
-  // {
-  //   path: '/main/MobileChat',
-  //   component: Layout,
-  //   redirect: '',
-  //   hidden: true,
-  //   children: [
-  //     {
-  //       path: '',
-  //       component: () => import('@/views/chatHome/chatHomeIndex/MobileChat'),
-  //       name: 'MobileChat',
-  //       meta: { title: 'AI瀵硅瘽', icon: 'dashboard', affix: true}
-  //     }
-  //   ]
-  // },
-  {
-    path: '/user',
-    component: Layout,
-    hidden: true,
-    redirect: 'noredirect',
-    children: [
-      {
-        path: 'profile',
-        component: () => import('@/views/system/user/profile/index'),
-        name: 'Profile',
-        meta: { title: '涓汉涓績', icon: 'user' }
-      }
-    ]
-  }
-]
-
-// 鍔ㄦ�佽矾鐢憋紝鍩轰簬鐢ㄦ埛鏉冮檺鍔ㄦ�佸幓鍔犺浇
-export const dynamicRoutes = [
-  {
-    path: '/system/user-auth',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:user:edit'],
-    children: [
-      {
-        path: 'role/:userId(\\d+)',
-        component: () => import('@/views/system/user/authRole'),
-        name: 'AuthRole',
-        meta: { title: '鍒嗛厤瑙掕壊', activeMenu: '/system/user' }
-      }
-    ]
-  },
-  {
-    path: '/system/role-auth',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:role:edit'],
-    children: [
-      {
-        path: 'user/:roleId(\\d+)',
-        component: () => import('@/views/system/role/authUser'),
-        name: 'AuthUser',
-        meta: { title: '鍒嗛厤鐢ㄦ埛', activeMenu: '/system/role' }
-      }
-    ]
-  },
-  {
-    path: '/system/dict-data',
-    component: Layout,
-    hidden: true,
-    permissions: ['system:dict:list'],
-    children: [
-      {
-        path: 'index/:dictId(\\d+)',
-        component: () => import('@/views/system/dict/data'),
-        name: 'Data',
-        meta: { title: '瀛楀吀鏁版嵁', activeMenu: '/system/dict' }
-      }
-    ]
-  },
-  {
-    path: '/monitor/job-log',
-    component: Layout,
-    hidden: true,
-    permissions: ['monitor:job:list'],
-    children: [
-      {
-        path: 'index/:jobId(\\d+)',
-        component: () => import('@/views/monitor/job/log'),
-        name: 'JobLog',
-        meta: { title: '璋冨害鏃ュ織', activeMenu: '/monitor/job' }
-      }
-    ]
-  },
-  {
-    path: '/tool/gen-edit',
-    component: Layout,
-    hidden: true,
-    permissions: ['tool:gen:edit'],
-    children: [
-      {
-        path: 'index/:tableId(\\d+)',
-        component: () => import('@/views/tool/gen/editTable'),
-        name: 'GenEdit',
-        meta: { title: '淇敼鐢熸垚閰嶇疆', activeMenu: '/tool/gen' }
-      }
-    ]
-  }
-]
-
-const router = createRouter({
-  history: createWebHistory(),
-  routes: constantRoutes,
-  scrollBehavior(to, from, savedPosition) {
-    if (savedPosition) {
-      return savedPosition
-    }
-    return { top: 0 }
-  },
-})
-
-export default router
+import { createWebHistory, createRouter } from 'vue-router'
+/* Layout */
+import Layout from '@/layout'
+
+/**
+ * Note: 璺敱閰嶇疆椤�
+ *
+ * hidden: true                     // 褰撹缃� true 鐨勬椂鍊欒璺敱涓嶄細鍐嶄晶杈规爮鍑虹幇 濡�401锛宭ogin绛夐〉闈紝鎴栬�呭涓�浜涚紪杈戦〉闈�/edit/1
+ * alwaysShow: true                 // 褰撲綘涓�涓矾鐢变笅闈㈢殑 children 澹版槑鐨勮矾鐢卞ぇ浜�1涓椂锛岃嚜鍔ㄤ細鍙樻垚宓屽鐨勬ā寮�--濡傜粍浠堕〉闈�
+ *                                  // 鍙湁涓�涓椂锛屼細灏嗛偅涓瓙璺敱褰撳仛鏍硅矾鐢辨樉绀哄湪渚ц竟鏍�--濡傚紩瀵奸〉闈�
+ *                                  // 鑻ヤ綘鎯充笉绠¤矾鐢变笅闈㈢殑 children 澹版槑鐨勪釜鏁伴兘鏄剧ず浣犵殑鏍硅矾鐢�
+ *                                  // 浣犲彲浠ヨ缃� alwaysShow: true锛岃繖鏍峰畠灏变細蹇界暐涔嬪墠瀹氫箟鐨勮鍒欙紝涓�鐩存樉绀烘牴璺敱
+ * redirect: noRedirect             // 褰撹缃� noRedirect 鐨勬椂鍊欒璺敱鍦ㄩ潰鍖呭睉瀵艰埅涓笉鍙鐐瑰嚮
+ * name:'router-name'               // 璁惧畾璺敱鐨勫悕瀛楋紝涓�瀹氳濉啓涓嶇劧浣跨敤<keep-alive>鏃朵細鍑虹幇鍚勭闂
+ * query: '{"id": 1, "name": "ry"}' // 璁块棶璺敱鐨勯粯璁や紶閫掑弬鏁�
+ * roles: ['admin', 'common']       // 璁块棶璺敱鐨勮鑹叉潈闄�
+ * permissions: ['a:a:a', 'b:b:b']  // 璁块棶璺敱鐨勮彍鍗曟潈闄�
+ * meta : {
+    noCache: true                   // 濡傛灉璁剧疆涓簍rue锛屽垯涓嶄細琚� <keep-alive> 缂撳瓨(榛樿 false)
+    title: 'title'                  // 璁剧疆璇ヨ矾鐢卞湪渚ц竟鏍忓拰闈㈠寘灞戜腑灞曠ず鐨勫悕瀛�
+    icon: 'svg-name'                // 璁剧疆璇ヨ矾鐢辩殑鍥炬爣锛屽搴旇矾寰剆rc/assets/icons/svg
+    breadcrumb: false               // 濡傛灉璁剧疆涓篺alse锛屽垯涓嶄細鍦╞readcrumb闈㈠寘灞戜腑鏄剧ず
+    activeMenu: '/system/user'      // 褰撹矾鐢辫缃簡璇ュ睘鎬э紝鍒欎細楂樹寒鐩稿搴旂殑渚ц竟鏍忋��
+  }
+ */
+
+// 鍏叡璺敱
+export const constantRoutes = [
+  {
+    path: '/redirect',
+    component: Layout,
+    hidden: true,
+    children: [
+      {
+        path: '/redirect/:path(.*)',
+        component: () => import('@/views/redirect/index.vue')
+      }
+    ]
+  },
+  {
+    path: '/login',
+    component: () => import('@/views/login'),
+    hidden: true
+  },
+  {
+    path: '/register',
+    component: () => import('@/views/register'),
+    hidden: true
+  },
+  {
+    path: "/:pathMatch(.*)*",
+    component: () => import('@/views/error/404'),
+    hidden: true
+  },
+  {
+    path: '/401',
+    component: () => import('@/views/error/401'),
+    hidden: true
+  },
+
+  {
+    path: '',
+    component: Layout,
+    redirect: '/index',
+    children: [
+      {
+        path: '/index',
+        component: () => import('@/views/index'),
+        name: 'Index',
+        meta: { title: '棣栭〉', icon: 'dashboard', affix: true }
+      }
+    ]
+  },
+  {
+    path: '/user',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'profile',
+        component: () => import('@/views/system/user/profile/index'),
+        name: 'Profile',
+        meta: { title: '涓汉涓績', icon: 'user' }
+      }
+    ]
+  }
+]
+
+// 鍔ㄦ�佽矾鐢憋紝鍩轰簬鐢ㄦ埛鏉冮檺鍔ㄦ�佸幓鍔犺浇
+export const dynamicRoutes = [
+  {
+    path: '/system/user-auth',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:user:edit'],
+    children: [
+      {
+        path: 'role/:userId(\\d+)',
+        component: () => import('@/views/system/user/authRole'),
+        name: 'AuthRole',
+        meta: { title: '鍒嗛厤瑙掕壊', activeMenu: '/system/user' }
+      }
+    ]
+  },
+  {
+    path: '/main/MobileChat',
+    component: Layout,
+    redirect: '',
+    hidden: true,
+    permissions: ['MobileChat:edit'],
+    children: [
+      {
+        path: '',
+        component: () => import('@/views/chatHome/chatHomeIndex/MobileChat'),
+        name: 'MobileChat',
+        meta: { title: 'AI瀵硅瘽', activeMenu: '/chatHome/chatHomeIndex'}
+      }
+    ]
+  },
+  {
+    path: '/system/role-auth',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:role:edit'],
+    children: [
+      {
+        path: 'user/:roleId(\\d+)',
+        component: () => import('@/views/system/role/authUser'),
+        name: 'AuthUser',
+        meta: { title: '鍒嗛厤鐢ㄦ埛', activeMenu: '/system/role' }
+      }
+    ]
+  },
+  {
+    path: '/system/dict-data',
+    component: Layout,
+    hidden: true,
+    permissions: ['system:dict:list'],
+    children: [
+      {
+        path: 'index/:dictId(\\d+)',
+        component: () => import('@/views/system/dict/data'),
+        name: 'Data',
+        meta: { title: '瀛楀吀鏁版嵁', activeMenu: '/system/dict' }
+      }
+    ]
+  },
+  {
+    path: '/monitor/job-log',
+    component: Layout,
+    hidden: true,
+    permissions: ['monitor:job:list'],
+    children: [
+      {
+        path: 'index/:jobId(\\d+)',
+        component: () => import('@/views/monitor/job/log'),
+        name: 'JobLog',
+        meta: { title: '璋冨害鏃ュ織', activeMenu: '/monitor/job' }
+      }
+    ]
+  },
+  {
+    path: '/tool/gen-edit',
+    component: Layout,
+    hidden: true,
+    permissions: ['tool:gen:edit'],
+    children: [
+      {
+        path: 'index/:tableId(\\d+)',
+        component: () => import('@/views/tool/gen/editTable'),
+        name: 'GenEdit',
+        meta: { title: '淇敼鐢熸垚閰嶇疆', activeMenu: '/tool/gen' }
+      }
+    ]
+  }
+]
+
+const router = createRouter({
+  history: createWebHistory(),
+  routes: constantRoutes,
+  scrollBehavior(to, from, savedPosition) {
+    if (savedPosition) {
+      return savedPosition
+    }
+    return { top: 0 }
+  },
+})
+
+export default router
diff --git a/src/views/collaborativeApproval/meetingBoard/index.vue b/src/views/collaborativeApproval/meetingBoard/index.vue
new file mode 100644
index 0000000..63c74f9
--- /dev/null
+++ b/src/views/collaborativeApproval/meetingBoard/index.vue
@@ -0,0 +1,498 @@
+<template>
+  <div class="app-container">
+    <!-- 椤甸潰鏍囬 -->
+    <div class="page-header">
+      <h2>浼氳鐪嬫澘</h2>
+<!--      <el-button type="primary" @click="createMeeting">鍒涘缓浼氳</el-button>-->
+    </div>
+
+    <!-- 浼氳缁熻鍗$墖 -->
+    <div class="stats-cards">
+      <el-card class="stat-card">
+        <div class="stat-content">
+          <div class="stat-number">{{ stats.total }}</div>
+          <div class="stat-label">鎬讳細璁暟</div>
+        </div>
+      </el-card>
+      <el-card class="stat-card">
+        <div class="stat-content">
+          <div class="stat-number">{{ stats.ongoing }}</div>
+          <div class="stat-label">杩涜涓�</div>
+        </div>
+      </el-card>
+      <el-card class="stat-card">
+        <div class="stat-content">
+          <div class="stat-number">{{ stats.completed }}</div>
+          <div class="stat-label">宸插畬鎴�</div>
+        </div>
+      </el-card>
+      <el-card class="stat-card">
+        <div class="stat-content">
+          <div class="stat-number">{{ stats.upcoming }}</div>
+          <div class="stat-label">鍗冲皢寮�濮�</div>
+        </div>
+      </el-card>
+    </div>
+
+    <!-- 浼氳鍒楄〃 -->
+    <div class="meeting-list">
+      <el-card v-for="meeting in meetings" :key="meeting.id" class="meeting-card">
+        <div class="meeting-header">
+          <div class="meeting-title">
+            <h3>{{ meeting.title }}</h3>
+            <el-tag :type="getStatusType(meeting.status)" size="small">
+              {{ getStatusText(meeting.status) }}
+            </el-tag>
+          </div>
+          <div class="meeting-time">
+            <el-icon><Clock /></el-icon>
+            {{ formatTime(meeting.startTime) }} - {{ formatTime(meeting.endTime) }}
+          </div>
+        </div>
+        
+        <div class="meeting-info">
+          <div class="info-item">
+            <el-icon><Location /></el-icon>
+            <span>{{ meeting.location }}</span>
+          </div>
+          <div class="info-item">
+            <el-icon><User /></el-icon>
+            <span>涓绘寔浜�: {{ meeting.host }}</span>
+          </div>
+          <div class="info-item">
+            <el-icon><UserFilled /></el-icon>
+            <span>鍙備細浜烘暟: {{ meeting.participants.length }}浜�</span>
+          </div>
+        </div>
+
+        <div class="meeting-agenda">
+          <h4>璁▼瀹夋帓</h4>
+          <div class="agenda-list">
+            <div 
+              v-for="(agenda, index) in meeting.agenda" 
+              :key="index"
+              class="agenda-item"
+              :class="{ 'active': agenda.status === 'active', 'completed': agenda.status === 'completed' }"
+            >
+              <span class="agenda-time">{{ agenda.time }}</span>
+              <span class="agenda-content">{{ agenda.content }}</span>
+              <el-tag 
+                :type="getAgendaStatusType(agenda.status)" 
+                size="small"
+              >
+                {{ getAgendaStatusText(agenda.status) }}
+              </el-tag>
+            </div>
+          </div>
+        </div>
+
+<!--        <div class="meeting-actions">-->
+<!--          <el-button type="primary" size="small" @click="joinMeeting(meeting)">-->
+<!--            鍔犲叆浼氳-->
+<!--          </el-button>-->
+<!--          <el-button type="info" size="small" @click="viewDetails(meeting)">-->
+<!--            鏌ョ湅璇︽儏-->
+<!--          </el-button>-->
+<!--          <el-button type="warning" size="small" @click="editMeeting(meeting)">-->
+<!--            缂栬緫-->
+<!--          </el-button>-->
+<!--        </div>-->
+      </el-card>
+    </div>
+
+    <!-- 鍒涘缓浼氳瀵硅瘽妗� -->
+    <el-dialog v-model="dialogVisible" title="鍒涘缓浼氳" width="600px">
+      <el-form :model="meetingForm" label-width="100px">
+        <el-form-item label="浼氳鏍囬">
+          <el-input v-model="meetingForm.title" placeholder="璇疯緭鍏ヤ細璁爣棰�" />
+        </el-form-item>
+        <el-form-item label="浼氳鏃堕棿">
+          <el-date-picker
+            v-model="meetingForm.timeRange"
+            type="datetimerange"
+            range-separator="鑷�"
+            start-placeholder="寮�濮嬫椂闂�"
+            end-placeholder="缁撴潫鏃堕棿"
+            format="YYYY-MM-DD HH:mm"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          />
+        </el-form-item>
+        <el-form-item label="浼氳鍦扮偣">
+          <el-input v-model="meetingForm.location" placeholder="璇疯緭鍏ヤ細璁湴鐐�" />
+        </el-form-item>
+        <el-form-item label="涓绘寔浜�">
+          <el-input v-model="meetingForm.host" placeholder="璇疯緭鍏ヤ富鎸佷汉濮撳悕" />
+        </el-form-item>
+        <el-form-item label="浼氳鎻忚堪">
+          <el-input
+            v-model="meetingForm.description"
+            type="textarea"
+            :rows="3"
+            placeholder="璇疯緭鍏ヤ細璁弿杩�"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="submitMeeting">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
+import { Clock, Location, User, UserFilled } from '@element-plus/icons-vue'
+
+// 缁熻鏁版嵁
+const stats = reactive({
+  total: 12,
+  ongoing: 3,
+  completed: 7,
+  upcoming: 2
+})
+
+// 浼氳鏁版嵁
+const meetings = ref([
+  {
+    id: 1,
+    title: '浜у搧寮�鍙戝懆浼�',
+    status: 'ongoing',
+    startTime: '2024-01-15 09:00:00',
+    endTime: '2024-01-15 10:30:00',
+    location: '浼氳瀹',
+    host: '寮犵粡鐞�',
+    participants: ['寮犵粡鐞�', '鏉庡伐绋嬪笀', '鐜嬭璁″笀', '璧垫祴璇曞憳'],
+    agenda: [
+      { time: '09:00-09:15', content: '涓婂懆宸ヤ綔鎬荤粨', status: 'completed' },
+      { time: '09:15-09:45', content: '鏈懆寮�鍙戣鍒�', status: 'active' },
+      { time: '09:45-10:00', content: '鎶�鏈毦鐐硅璁�', status: 'pending' },
+      { time: '10:00-10:30', content: '闂鍙嶉涓庤В鍐�', status: 'pending' }
+    ]
+  },
+  {
+    id: 2,
+    title: '瀹㈡埛闇�姹傝瘎瀹′細',
+    status: 'upcoming',
+    startTime: '2024-01-15 14:00:00',
+    endTime: '2024-01-15 15:00:00',
+    location: '绾夸笂浼氳',
+    host: '闄堟�荤洃',
+    participants: ['闄堟�荤洃', '鍒樹骇鍝佺粡鐞�', '瀛欏鎴风粡鐞�', '瀹㈡埛浠h〃'],
+    agenda: [
+      { time: '14:00-14:20', content: '闇�姹傝儗鏅粙缁�', status: 'pending' },
+      { time: '14:20-14:40', content: '鍔熻兘闇�姹傚垎鏋�', status: 'pending' },
+      { time: '14:40-15:00', content: '鎶�鏈彲琛屾�ц瘎浼�', status: 'pending' }
+    ]
+  },
+  {
+    id: 3,
+    title: '鍥㈤槦寤鸿娲诲姩',
+    status: 'completed',
+    startTime: '2024-01-14 16:00:00',
+    endTime: '2024-01-14 18:00:00',
+    location: '鍏徃澶у巺',
+    host: '浜轰簨閮�',
+    participants: ['鍏ㄤ綋鍛樺伐'],
+    agenda: [
+      { time: '16:00-16:30', content: '鍥㈤槦娓告垙', status: 'completed' },
+      { time: '16:30-17:00', content: '缁忛獙鍒嗕韩', status: 'completed' },
+      { time: '17:00-18:00', content: '鑷敱浜ゆ祦', status: 'completed' }
+    ]
+  }
+])
+
+// 瀵硅瘽妗嗙浉鍏�
+const dialogVisible = ref(false)
+const meetingForm = reactive({
+  title: '',
+  timeRange: [],
+  location: '',
+  host: '',
+  description: ''
+})
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+  const statusMap = {
+    'ongoing': 'success',
+    'upcoming': 'warning',
+    'completed': 'info'
+  }
+  return statusMap[status] || 'info'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+  const statusMap = {
+    'ongoing': '杩涜涓�',
+    'upcoming': '鍗冲皢寮�濮�',
+    'completed': '宸插畬鎴�'
+  }
+  return statusMap[status] || '鏈煡'
+}
+
+// 鑾峰彇璁▼鐘舵�佺被鍨�
+const getAgendaStatusType = (status) => {
+  const statusMap = {
+    'completed': 'success',
+    'active': 'warning',
+    'pending': 'info'
+  }
+  return statusMap[status] || 'info'
+}
+
+// 鑾峰彇璁▼鐘舵�佹枃鏈�
+const getAgendaStatusText = (status) => {
+  const statusMap = {
+    'completed': '宸插畬鎴�',
+    'active': '杩涜涓�',
+    'pending': '寰呭紑濮�'
+  }
+  return statusMap[status] || '鏈煡'
+}
+
+// 鏍煎紡鍖栨椂闂�
+const formatTime = (timeStr) => {
+  const date = new Date(timeStr)
+  return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
+}
+
+// 鍒涘缓浼氳
+const createMeeting = () => {
+  dialogVisible.value = true
+  // 閲嶇疆琛ㄥ崟
+  Object.assign(meetingForm, {
+    title: '',
+    timeRange: [],
+    location: '',
+    host: '',
+    description: ''
+  })
+}
+
+// 鎻愪氦浼氳
+const submitMeeting = () => {
+  if (!meetingForm.title || !meetingForm.timeRange.length || !meetingForm.location || !meetingForm.host) {
+    ElMessage.warning('璇峰~鍐欏畬鏁寸殑浼氳淇℃伅')
+    return
+  }
+  
+  // 鍒涘缓鏂颁細璁�
+  const newMeeting = {
+    id: Date.now(),
+    title: meetingForm.title,
+    status: 'upcoming',
+    startTime: meetingForm.timeRange[0],
+    endTime: meetingForm.timeRange[1],
+    location: meetingForm.location,
+    host: meetingForm.host,
+    participants: [meetingForm.host],
+    agenda: [
+      { time: '寰呭畾', content: '璁▼寰呭畾', status: 'pending' }
+    ]
+  }
+  
+  meetings.value.unshift(newMeeting)
+  stats.total++
+  stats.upcoming++
+  
+  ElMessage.success('浼氳鍒涘缓鎴愬姛')
+  dialogVisible.value = false
+}
+
+// 鍔犲叆浼氳
+const joinMeeting = (meeting) => {
+  ElMessage.success(`宸插姞鍏ヤ細璁細${meeting.title}`)
+}
+
+// 鏌ョ湅璇︽儏
+const viewDetails = (meeting) => {
+  ElMessage.info(`鏌ョ湅浼氳璇︽儏锛�${meeting.title}`)
+}
+
+// 缂栬緫浼氳
+const editMeeting = (meeting) => {
+  ElMessage.info(`缂栬緫浼氳锛�${meeting.title}`)
+}
+
+onMounted(() => {
+  console.log('浼氳鐪嬫澘椤甸潰鍔犺浇瀹屾垚')
+})
+</script>
+
+<style scoped>
+.app-container {
+  padding: 20px;
+}
+
+.page-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.page-header h2 {
+  margin: 0;
+  color: #303133;
+}
+
+.stats-cards {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+  gap: 20px;
+  margin-bottom: 30px;
+}
+
+.stat-card {
+  text-align: center;
+}
+
+.stat-content {
+  padding: 10px;
+}
+
+.stat-number {
+  font-size: 32px;
+  font-weight: bold;
+  color: #409eff;
+  margin-bottom: 8px;
+}
+
+.stat-label {
+  font-size: 14px;
+  color: #606266;
+}
+
+.meeting-list {
+  display: grid;
+  gap: 20px;
+}
+
+.meeting-card {
+  border-radius: 8px;
+}
+
+.meeting-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  margin-bottom: 15px;
+}
+
+.meeting-title {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.meeting-title h3 {
+  margin: 0;
+  color: #303133;
+}
+
+.meeting-time {
+  display: flex;
+  align-items: center;
+  gap: 5px;
+  color: #606266;
+  font-size: 14px;
+}
+
+.meeting-info {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 20px;
+  flex-wrap: wrap;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  gap: 5px;
+  color: #606266;
+  font-size: 14px;
+}
+
+.meeting-agenda {
+  margin-bottom: 20px;
+}
+
+.meeting-agenda h4 {
+  margin: 0 0 15px 0;
+  color: #303133;
+  font-size: 16px;
+}
+
+.agenda-list {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.agenda-item {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  padding: 10px;
+  border-radius: 6px;
+  background-color: #f5f7fa;
+}
+
+.agenda-item.active {
+  background-color: #fdf6ec;
+  border-left: 3px solid #e6a23c;
+}
+
+.agenda-item.completed {
+  background-color: #f0f9ff;
+  border-left: 3px solid #409eff;
+}
+
+.agenda-time {
+  font-weight: bold;
+  color: #606266;
+  min-width: 80px;
+}
+
+.agenda-content {
+  flex: 1;
+  color: #303133;
+}
+
+.meeting-actions {
+  display: flex;
+  gap: 10px;
+  justify-content: flex-end;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+
+@media (max-width: 768px) {
+  .stats-cards {
+    grid-template-columns: repeat(2, 1fr);
+  }
+  
+  .meeting-header {
+    flex-direction: column;
+    gap: 10px;
+  }
+  
+  .meeting-info {
+    flex-direction: column;
+    gap: 10px;
+  }
+  
+  .meeting-actions {
+    flex-direction: column;
+  }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/noticeManagement/index.vue b/src/views/collaborativeApproval/noticeManagement/index.vue
new file mode 100644
index 0000000..daa4cd7
--- /dev/null
+++ b/src/views/collaborativeApproval/noticeManagement/index.vue
@@ -0,0 +1,705 @@
+<template>
+  <div class="app-container">
+    <!-- 鎼滅储琛ㄥ崟 -->
+    <div class="search_form">
+      <div>
+        <span class="search_title">鍏憡鏍囬锛�</span>
+        <el-input
+          v-model="searchForm.noticeTitle"
+          style="width: 240px"
+          placeholder="璇疯緭鍏ュ叕鍛婃爣棰樻悳绱�"
+          @change="handleQuery"
+          clearable
+          :prefix-icon="Search"
+        />
+        <span class="search_title ml10">鍏憡绫诲瀷锛�</span>
+        <el-select v-model="searchForm.noticeType" clearable @change="handleQuery" style="width: 240px">
+          <el-option label="鏀惧亣閫氱煡" value="1" />
+          <el-option label="璁惧缁翠慨閫氱煡" value="2" />
+        </el-select>
+        <span class="search_title ml10">鐘舵�侊細</span>
+        <el-select v-model="searchForm.status" clearable @change="handleQuery" style="width: 240px">
+          <el-option label="鑽夌" value="0" />
+          <el-option label="宸插彂甯�" value="1" />
+          <el-option label="宸蹭笅绾�" value="2" />
+        </el-select>
+        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">鎼滅储</el-button>
+        <el-button @click="resetQuery" style="margin-left: 10px">閲嶇疆</el-button>
+      </div>
+      <div>
+        <el-button type="primary" @click="openForm('add')">鏂板鍏憡</el-button>
+        <el-button type="danger" plain @click="handleDelete" :disabled="!selectedIds.length">鍒犻櫎</el-button>
+      </div>
+    </div>
+
+    <!-- 閫氱煡鍏憡鏉� -->
+    <div class="notice-board">
+      <!-- 鏀惧亣閫氱煡鍖哄煙 -->
+      <div class="notice-section" v-if="holidayNotices.length > 0">
+        <div class="section-header">
+          <h3>馃搮 鏀惧亣閫氱煡</h3>
+          <span class="section-count">{{ holidayNotices.length }}鏉�</span>
+        </div>
+        <div class="notice-cards">
+          <div 
+            v-for="notice in holidayNotices" 
+            :key="notice.id"
+            class="notice-card holiday-card"
+            :class="{ 'urgent': notice.priority === '3' }"
+          >
+            <div class="card-header">
+              <div class="card-title">
+                <el-icon class="holiday-icon"><Calendar /></el-icon>
+                {{ notice.noticeTitle }}
+              </div>
+              <div class="card-actions">
+                <el-button link type="primary" @click="handleEdit(notice)">缂栬緫</el-button>
+                <el-button link type="danger" @click="handleDelete(notice.id)">鍒犻櫎</el-button>
+              </div>
+            </div>
+            <div class="card-content">
+              <p>{{ notice.noticeContent }}</p>
+            </div>
+            <div class="card-footer">
+              <div class="card-meta">
+                <span class="priority" :class="'priority-' + notice.priority">
+                  {{ getPriorityText(notice.priority) }}
+                </span>
+                <span class="status" :class="'status-' + notice.status">
+                  {{ getStatusText(notice.status) }}
+                </span>
+              </div>
+              <div class="card-info">
+                <span class="creator">{{ notice.createBy }}</span>
+                <span class="time">{{ notice.createTime }}</span>
+              </div>
+            </div>
+            <div class="card-remark" v-if="notice.remark">
+              <el-icon><InfoFilled /></el-icon>
+              <span>{{ notice.remark }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 璁惧缁翠慨閫氱煡鍖哄煙 -->
+      <div class="notice-section" v-if="maintenanceNotices.length > 0">
+        <div class="section-header">
+          <h3>馃敡 璁惧缁翠慨閫氱煡</h3>
+          <span class="section-count">{{ maintenanceNotices.length }}鏉�</span>
+        </div>
+        <div class="notice-cards">
+          <div 
+            v-for="notice in maintenanceNotices" 
+            :key="notice.id"
+            class="notice-card maintenance-card"
+            :class="{ 'urgent': notice.priority === '3' }"
+          >
+            <div class="card-header">
+              <div class="card-title">
+                <el-icon class="maintenance-icon"><Tools /></el-icon>
+                {{ notice.noticeTitle }}
+              </div>
+              <div class="card-actions">
+                <el-button link type="primary" @click="handleEdit(notice)">缂栬緫</el-button>
+                <el-button link type="danger" @click="handleDelete(notice.id)">鍒犻櫎</el-button>
+              </div>
+            </div>
+            <div class="card-content">
+              <p>{{ notice.noticeContent }}</p>
+            </div>
+            <div class="card-footer">
+              <div class="card-meta">
+                <span class="priority" :class="'priority-' + notice.priority">
+                  {{ getPriorityText(notice.priority) }}
+                </span>
+                <span class="status" :class="'status-' + notice.status">
+                  {{ getStatusText(notice.status) }}
+                </span>
+              </div>
+              <div class="card-info">
+                <span class="creator">{{ notice.createBy }}</span>
+                <span class="time">{{ notice.createTime }}</span>
+              </div>
+            </div>
+            <div class="card-remark" v-if="notice.remark">
+              <el-icon><InfoFilled /></el-icon>
+              <span>{{ notice.remark }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 绌虹姸鎬� -->
+      <div class="empty-state" v-if="filteredNotices.length === 0">
+        <el-empty description="鏆傛棤閫氱煡鍏憡" />
+      </div>
+    </div>
+
+    <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog 
+      :title="dialogTitle" 
+      v-model="dialogVisible" 
+      width="800px" 
+      append-to-body
+      @close="resetForm"
+    >
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鍏憡鏍囬" prop="noticeTitle">
+              <el-input v-model="form.noticeTitle" placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍏憡绫诲瀷" prop="noticeType">
+              <el-select v-model="form.noticeType" placeholder="璇烽�夋嫨鍏憡绫诲瀷" style="width: 100%">
+                <el-option label="鏀惧亣閫氱煡" value="1" />
+                <el-option label="璁惧缁翠慨閫氱煡" value="2" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="鐘舵��">
+              <el-radio-group v-model="form.status">
+                <el-radio value="0">鑽夌</el-radio>
+                <el-radio value="1">宸插彂甯�</el-radio>
+                <el-radio value="2">宸蹭笅绾�</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浼樺厛绾�">
+              <el-select v-model="form.priority" placeholder="璇烽�夋嫨浼樺厛绾�" style="width: 100%">
+                <el-option label="鏅��" value="1" />
+                <el-option label="閲嶈" value="2" />
+                <el-option label="绱ф��" value="3" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="鍏憡鍐呭" prop="noticeContent">
+              <el-input
+                v-model="form.noticeContent"
+                type="textarea"
+                :rows="6"
+                placeholder="璇疯緭鍏ュ叕鍛婂唴瀹�"
+                maxlength="500"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="澶囨敞">
+              <el-input
+                v-model="form.remark"
+                type="textarea"
+                :rows="3"
+                placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
+                maxlength="200"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="dialogVisible = false">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { Search, Calendar, Tools, InfoFilled } from "@element-plus/icons-vue";
+import { onMounted, ref, reactive, toRefs, computed } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import useUserStore from "@/store/modules/user";
+
+const userStore = useUserStore();
+
+// 鍝嶅簲寮忔暟鎹�
+const data = reactive({
+  searchForm: {
+    noticeTitle: "",
+    noticeType: "",
+    status: "",
+  },
+  form: {
+    id: undefined,
+    noticeTitle: "",
+    noticeType: "",
+    noticeContent: "",
+    status: "0",
+    priority: "1",
+    remark: "",
+    createBy: "",
+    createTime: "",
+  },
+  rules: {
+    noticeTitle: [
+      { required: true, message: "鍏憡鏍囬涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    noticeType: [
+      { required: true, message: "璇烽�夋嫨鍏憡绫诲瀷", trigger: "change" }
+    ],
+    noticeContent: [
+      { required: true, message: "鍏憡鍐呭涓嶈兘涓虹┖", trigger: "blur" }
+    ]
+  }
+});
+
+const { searchForm, form, rules } = toRefs(data);
+
+// 椤甸潰鐘舵��
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const selectedIds = ref([]);
+const formRef = ref();
+
+// 妯℃嫙鏁版嵁 - 鏍规嵁娉曞畾鑺傚亣鏃ヨ璁�
+const mockData = [
+  {
+    id: 1,
+    noticeTitle: "2024骞存槬鑺傛斁鍋囬�氱煡",
+    noticeType: "1",
+    priority: "2",
+    status: "1",
+    noticeContent: "鏍规嵁鍥藉姟闄㈠姙鍏巺閫氱煡锛�2024骞存槬鑺傛斁鍋囧畨鎺掑涓嬶細2鏈�10鏃ワ紙鍒濅竴锛夎嚦2鏈�17鏃ワ紙鍒濆叓锛夋斁鍋囪皟浼戯紝鍏�8澶┿��2鏈�4鏃ワ紙鏄熸湡鏃ワ級銆�2鏈�18鏃ワ紙鏄熸湡鏃ワ級涓婄彮銆傝鍚勯儴闂ㄦ彁鍓嶅仛濂藉伐浣滃畨鎺掋��",
+    remark: "鏀惧亣鏈熼棿璇蜂繚鎸佹墜鏈虹晠閫氾紝濡傛湁绱ф�ヤ簨鍔″強鏃惰仈绯�",
+    createBy: "浜轰簨閮�",
+    createTime: "2024-01-15 10:30:00"
+  },
+  {
+    id: 2,
+    noticeTitle: "2024骞存竻鏄庤妭鏀惧亣閫氱煡",
+    noticeType: "1",
+    priority: "1",
+    status: "1",
+    noticeContent: "鏍规嵁鍥藉姟闄㈠姙鍏巺閫氱煡锛�2024骞存竻鏄庤妭鏀惧亣瀹夋帓濡備笅锛�4鏈�4鏃ワ紙鏄熸湡鍥涳級鑷�4鏈�6鏃ワ紙鏄熸湡鍏級鏀惧亣璋冧紤锛屽叡3澶┿��4鏈�7鏃ワ紙鏄熸湡鏃ワ級涓婄彮銆�",
+    remark: "璇峰悇閮ㄩ棬鍋氬ソ鍊肩彮瀹夋帓锛岀‘淇濊妭鏃ユ湡闂村悇椤瑰伐浣滄甯歌繍杞�",
+    createBy: "琛屾斂閮�",
+    createTime: "2024-01-14 14:20:00"
+  },
+  {
+    id: 3,
+    noticeTitle: "2024骞村姵鍔ㄨ妭鏀惧亣閫氱煡",
+    noticeType: "1",
+    priority: "1",
+    status: "1",
+    noticeContent: "鏍规嵁鍥藉姟闄㈠姙鍏巺閫氱煡锛�2024骞村姵鍔ㄨ妭鏀惧亣瀹夋帓濡備笅锛�5鏈�1鏃ワ紙鏄熸湡涓夛級鑷�5鏈�5鏃ワ紙鏄熸湡鏃ワ級鏀惧亣璋冧紤锛屽叡5澶┿��4鏈�28鏃ワ紙鏄熸湡鏃ワ級銆�5鏈�11鏃ワ紙鏄熸湡鍏級涓婄彮銆�",
+    remark: "鏀惧亣鍓嶈鍏抽棴鐢垫簮锛岄攣濂介棬绐楋紝娉ㄦ剰瀹夊叏",
+    createBy: "琛屾斂閮�",
+    createTime: "2024-01-13 09:15:00"
+  },
+  {
+    id: 4,
+    noticeTitle: "2024骞寸鍗堣妭鏀惧亣閫氱煡",
+    noticeType: "1",
+    priority: "1",
+    status: "1",
+    noticeContent: "鏍规嵁鍥藉姟闄㈠姙鍏巺閫氱煡锛�2024骞寸鍗堣妭鏀惧亣瀹夋帓濡備笅锛�6鏈�8鏃ワ紙鏄熸湡鍏級鑷�6鏈�10鏃ワ紙鏄熸湡涓�锛夋斁鍋囪皟浼戯紝鍏�3澶┿��6鏈�11鏃ワ紙鏄熸湡浜岋級涓婄彮銆�",
+    remark: "绁濆ぇ瀹剁鍗堣妭蹇箰锛岄槚瀹跺垢绂忥紒",
+    createBy: "琛屾斂閮�",
+    createTime: "2024-01-12 16:30:00"
+  },
+  {
+    id: 5,
+    noticeTitle: "2024骞翠腑绉嬭妭鏀惧亣閫氱煡",
+    noticeType: "1",
+    priority: "1",
+    status: "1",
+    noticeContent: "鏍规嵁鍥藉姟闄㈠姙鍏巺閫氱煡锛�2024骞翠腑绉嬭妭鏀惧亣瀹夋帓濡備笅锛�9鏈�15鏃ワ紙鏄熸湡鏃ワ級鑷�9鏈�17鏃ワ紙鏄熸湡浜岋級鏀惧亣璋冧紤锛屽叡3澶┿��9鏈�14鏃ワ紙鏄熸湡鍏級涓婄彮銆�",
+    remark: "涓浣宠妭锛岀澶у鍥㈠渾缇庢弧锛屽垢绂忓畨搴凤紒",
+    createBy: "琛屾斂閮�",
+    createTime: "2024-01-11 11:20:00"
+  },
+  {
+    id: 6,
+    noticeTitle: "2024骞村浗搴嗚妭鏀惧亣閫氱煡",
+    noticeType: "1",
+    priority: "2",
+    status: "1",
+    noticeContent: "鏍规嵁鍥藉姟闄㈠姙鍏巺閫氱煡锛�2024骞村浗搴嗚妭鏀惧亣瀹夋帓濡備笅锛�10鏈�1鏃ワ紙鏄熸湡浜岋級鑷�10鏈�7鏃ワ紙鏄熸湡涓�锛夋斁鍋囪皟浼戯紝鍏�7澶┿��9鏈�29鏃ワ紙鏄熸湡鏃ワ級銆�10鏈�12鏃ワ紙鏄熸湡鍏級涓婄彮銆�",
+    remark: "鍥藉簡鏈熼棿璇峰悇閮ㄩ棬鍋氬ソ鍊肩彮瀹夋帓锛岀‘淇濆畨鍏ㄧǔ瀹�",
+    createBy: "琛屾斂閮�",
+    createTime: "2024-01-10 15:45:00"
+  },
+  {
+    id: 7,
+    noticeTitle: "A杞﹂棿鐢熶骇绾垮勾搴︽淇�氱煡",
+    noticeType: "2",
+    priority: "2",
+    status: "1",
+    noticeContent: "A杞﹂棿鐢熶骇绾垮皢浜�2024骞�1鏈�20鏃ワ紙鍛ㄥ叚锛夎繘琛屽勾搴︽淇淮鎶わ紝棰勮鍋滃伐8灏忔椂銆傛淇唴瀹瑰寘鎷細璁惧娓呮磥銆佹鼎婊戜繚鍏汇�佸畨鍏ㄨ缃鏌ョ瓑銆傝鐢熶骇閮ㄩ棬鎻愬墠璋冩暣鐢熶骇璁″垝銆�",
+    remark: "缁翠慨鏈熼棿璇风浉鍏充汉鍛橀厤鍚堬紝纭繚妫�淇伐浣滃畨鍏ㄩ『鍒╄繘琛�",
+    createBy: "璁惧閮�",
+    createTime: "2024-01-14 14:20:00"
+  },
+  {
+    id: 8,
+    noticeTitle: "B杞﹂棿璁惧棰勯槻鎬х淮鎶ら�氱煡",
+    noticeType: "2",
+    priority: "1",
+    status: "1",
+    noticeContent: "B杞﹂棿鍏抽敭璁惧灏嗕簬2024骞�1鏈�25鏃ヨ繘琛岄闃叉�х淮鎶わ紝棰勮鍋滃伐4灏忔椂銆傜淮鎶ゅ唴瀹瑰寘鎷細璁惧妫�鏌ャ�侀浂浠舵洿鎹€�佹�ц兘娴嬭瘯绛夈�傝鐩稿叧閮ㄩ棬閰嶅悎銆�",
+    remark: "缁存姢瀹屾垚鍚庡皢杩涜璇曡繍琛岋紝纭繚璁惧姝e父杩愯",
+    createBy: "璁惧閮�",
+    createTime: "2024-01-13 09:15:00"
+  }
+];
+
+// 璁$畻灞炴��
+const filteredNotices = computed(() => {
+  let filtered = [...mockData];
+  
+  if (searchForm.value.noticeTitle) {
+    filtered = filtered.filter(item => 
+      item.noticeTitle.includes(searchForm.value.noticeTitle)
+    );
+  }
+  if (searchForm.value.noticeType) {
+    filtered = filtered.filter(item => 
+      item.noticeType === searchForm.value.noticeType
+    );
+  }
+  if (searchForm.value.status !== "") {
+    filtered = filtered.filter(item => 
+      item.status === searchForm.value.status
+    );
+  }
+  
+  return filtered;
+});
+
+const holidayNotices = computed(() => {
+  return filteredNotices.value.filter(notice => notice.noticeType === "1");
+});
+
+const maintenanceNotices = computed(() => {
+  return filteredNotices.value.filter(notice => notice.noticeType === "2");
+});
+
+// 鏂规硶瀹氫箟
+const handleQuery = () => {
+  // 鎼滅储鍔熻兘淇濇寔涓嶅彉锛屼絾鏁版嵁閫氳繃璁$畻灞炴�ц嚜鍔ㄨ繃婊�
+};
+
+const resetQuery = () => {
+  searchForm.value = {
+    noticeTitle: "",
+    noticeType: "",
+    status: ""
+  };
+};
+
+const getPriorityText = (priority) => {
+  const priorityMap = { "1": "鏅��", "2": "閲嶈", "3": "绱ф��" };
+  return priorityMap[priority] || "鏅��";
+};
+
+const getStatusText = (status) => {
+  const statusMap = { "0": "鑽夌", "1": "宸插彂甯�", "2": "宸蹭笅绾�" };
+  return statusMap[status] || "鏈煡";
+};
+
+const openForm = (type) => {
+  if (type === 'add') {
+    dialogTitle.value = "鏂板鍏憡";
+    form.value = {
+      id: undefined,
+      noticeTitle: "",
+      noticeType: "",
+      noticeContent: "",
+      status: "0",
+      priority: "1",
+      remark: "",
+      createBy: userStore.name || "褰撳墠鐢ㄦ埛",
+      createTime: new Date().toLocaleString()
+    };
+  }
+  dialogVisible.value = true;
+};
+
+const handleEdit = (row) => {
+  dialogTitle.value = "缂栬緫鍏憡";
+  form.value = { ...row };
+  dialogVisible.value = true;
+};
+
+const handleSelectionChange = (selection) => {
+  selectedIds.value = selection.map(item => item.id);
+};
+
+const handleDelete = (id) => {
+  ElMessageBox.confirm(
+    "纭鍒犻櫎杩欐潯鍏憡鍚楋紵",
+    "鎻愮ず",
+    {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning"
+    }
+  ).then(() => {
+    const index = mockData.findIndex(item => item.id === id);
+    if (index > -1) {
+      mockData.splice(index, 1);
+      ElMessage.success("鍒犻櫎鎴愬姛");
+    }
+  });
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      if (form.value.id) {
+        // 缂栬緫妯″紡
+        const index = mockData.findIndex(item => item.id === form.value.id);
+        if (index > -1) {
+          mockData[index] = { ...form.value };
+        }
+        ElMessage.success("淇敼鎴愬姛");
+      } else {
+        // 鏂板妯″紡
+        const newId = Math.max(...mockData.map(item => item.id)) + 1;
+        const newNotice = {
+          ...form.value,
+          id: newId,
+          createTime: new Date().toLocaleString()
+        };
+        mockData.unshift(newNotice);
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+    }
+  });
+};
+
+const resetForm = () => {
+  formRef.value?.resetFields();
+};
+
+// 鐢熷懡鍛ㄦ湡
+onMounted(() => {
+  // 椤甸潰鍔犺浇瀹屾垚
+});
+</script>
+
+<style scoped>
+.search_form {
+  background: #fff;
+  padding: 20px;
+  margin-bottom: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.search_title {
+  font-weight: 500;
+  color: #333;
+  margin-right: 8px;
+}
+
+.ml10 {
+  margin-left: 10px;
+}
+
+.notice-board {
+  background: #f5f7fa;
+  padding: 20px;
+  border-radius: 8px;
+}
+
+.notice-section {
+  margin-bottom: 30px;
+}
+
+.section-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 20px;
+  padding: 0 10px;
+}
+
+.section-header h3 {
+  margin: 0;
+  color: #303133;
+  font-size: 18px;
+  font-weight: 600;
+}
+
+.section-count {
+  margin-left: 10px;
+  background: #409eff;
+  color: white;
+  padding: 2px 8px;
+  border-radius: 12px;
+  font-size: 12px;
+}
+
+.notice-cards {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
+  gap: 20px;
+}
+
+.notice-card {
+  background: white;
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  transition: all 0.3s ease;
+  border-left: 4px solid transparent;
+}
+
+.notice-card:hover {
+  transform: translateY(-2px);
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
+}
+
+.holiday-card {
+  border-left-color: #67c23a;
+}
+
+.maintenance-card {
+  border-left-color: #e6a23c;
+}
+
+.urgent {
+  border-left-color: #f56c6c;
+  background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%);
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  margin-bottom: 15px;
+}
+
+.card-title {
+  display: flex;
+  align-items: center;
+  font-size: 16px;
+  font-weight: 600;
+  color: #303133;
+  flex: 1;
+}
+
+.holiday-icon {
+  color: #67c23a;
+  margin-right: 8px;
+  font-size: 18px;
+}
+
+.maintenance-icon {
+  color: #e6a23c;
+  margin-right: 8px;
+  font-size: 18px;
+}
+
+.card-actions {
+  display: flex;
+  gap: 8px;
+}
+
+.card-content {
+  margin-bottom: 15px;
+}
+
+.card-content p {
+  margin: 0;
+  color: #606266;
+  line-height: 1.6;
+  font-size: 14px;
+}
+
+.card-footer {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.card-meta {
+  display: flex;
+  gap: 8px;
+}
+
+.priority, .status {
+  padding: 2px 8px;
+  border-radius: 12px;
+  font-size: 12px;
+  font-weight: 500;
+}
+
+.priority-1 { background: #f0f9ff; color: #0369a1; }
+.priority-2 { background: #fef3c7; color: #d97706; }
+.priority-3 { background: #fef2f2; color: #dc2626; }
+
+.status-0 { background: #f3f4f6; color: #6b7280; }
+.status-1 { background: #d1fae5; color: #059669; }
+.status-2 { background: #fef3c7; color: #d97706; }
+
+.card-info {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-end;
+  font-size: 12px;
+  color: #909399;
+}
+
+.creator {
+  font-weight: 500;
+  margin-bottom: 2px;
+}
+
+.card-remark {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  padding: 8px 12px;
+  background: #f8f9fa;
+  border-radius: 6px;
+  font-size: 12px;
+  color: #606266;
+  border-left: 3px solid #409eff;
+}
+
+.empty-state {
+  text-align: center;
+  padding: 60px 20px;
+}
+
+.dialog-footer {
+  text-align: right;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 768px) {
+  .notice-cards {
+    grid-template-columns: 1fr;
+  }
+  
+  .search_form {
+    flex-direction: column;
+    gap: 15px;
+  }
+  
+  .search_form > div {
+    width: 100%;
+  }
+}
+</style>
diff --git a/src/views/collaborativeApproval/warningSystem/index.vue b/src/views/collaborativeApproval/warningSystem/index.vue
new file mode 100644
index 0000000..b04c583
--- /dev/null
+++ b/src/views/collaborativeApproval/warningSystem/index.vue
@@ -0,0 +1,307 @@
+<template>
+  <div class="warning-system">
+    <h2>棰勮鑱斿姩鏈哄埗</h2>
+    
+    <!-- 缁熻鍗$墖 -->
+    <div class="stats">
+      <div class="stat-card red">
+        <span class="number">2</span>
+        <span class="label">绾㈣壊棰勮</span>
+      </div>
+      <div class="stat-card orange">
+        <span class="number">1</span>
+        <span class="label">姗欒壊棰勮</span>
+      </div>
+      <div class="stat-card yellow">
+        <span class="number">1</span>
+        <span class="label">榛勮壊棰勮</span>
+      </div>
+      <div class="stat-card green">
+        <span class="number">1</span>
+        <span class="label">缁胯壊棰勮</span>
+      </div>
+    </div>
+
+    <!-- 棰勮鍒楄〃 -->
+    <div class="warning-list">
+      <h3>棰勮鍒楄〃</h3>
+      <table>
+        <thead>
+          <tr>
+            <th>缂栧彿</th>
+            <th>鏍囬</th>
+            <th>绫诲瀷</th>
+            <th>绛夌骇</th>
+            <th>鐘舵��</th>
+            <th>璐d换浜�</th>
+            <th>鎿嶄綔</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="warning in warnings" :key="warning.id">
+            <td>{{ warning.id }}</td>
+            <td>{{ warning.title }}</td>
+            <td>{{ warning.type }}</td>
+            <td>
+              <span :class="['level-tag', warning.level]">
+                {{ warning.levelText }}
+              </span>
+            </td>
+            <td>
+              <span :class="['status-tag', warning.status]">
+                {{ warning.statusText }}
+              </span>
+            </td>
+            <td>{{ warning.responsible }}</td>
+            <td>
+              <button @click="viewDetail(warning)">鏌ョ湅璇︽儏</button>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 璇︽儏瀵硅瘽妗� -->
+    <div v-if="showDetail" class="modal">
+      <div class="modal-content">
+        <h3>棰勮璇︽儏</h3>
+        <div v-if="currentWarning">
+          <p><strong>缂栧彿锛�</strong>{{ currentWarning.id }}</p>
+          <p><strong>鏍囬锛�</strong>{{ currentWarning.title }}</p>
+          <p><strong>绫诲瀷锛�</strong>{{ currentWarning.type }}</p>
+          <p><strong>绛夌骇锛�</strong>{{ currentWarning.levelText }}</p>
+          <p><strong>鎻忚堪锛�</strong>{{ currentWarning.description }}</p>
+          <p><strong>褰卞搷锛�</strong>{{ currentWarning.impact }}</p>
+          <p><strong>寤鸿锛�</strong>{{ currentWarning.suggestions }}</p>
+        </div>
+        <button @click="showDetail = false">鍏抽棴</button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'WarningSystem',
+  data() {
+    return {
+      showDetail: false,
+      currentWarning: null,
+      warnings: [
+        {
+          id: 'W001',
+          title: '椤圭洰棰勭畻瓒呮敮棰勮',
+          type: '璐㈠姟棰勮',
+          level: 'red',
+          levelText: '绾㈣壊棰勮',
+          status: 'pending',
+          statusText: '寰呭鐞�',
+          responsible: '寮犵粡鐞�',
+          description: 'A椤圭洰棰勭畻鎵ц鐜囧凡杈�95%锛岄璁″皢瓒呭嚭棰勭畻鑼冨洿銆�',
+          impact: '褰卞搷椤圭洰鏁翠綋璐㈠姟鎸囨爣锛屽彲鑳藉鑷撮」鐩簭鎹�',
+          suggestions: '鏆傚仠闈炲繀瑕佹敮鍑猴紝浼樺寲璧勬簮閰嶇疆锛岀敵璇烽绠楄皟鏁�'
+        },
+        {
+          id: 'W002',
+          title: '鍚堝悓鍒版湡棰勮',
+          type: '鍚堣棰勮',
+          level: 'orange',
+          levelText: '姗欒壊棰勮',
+          status: 'processing',
+          statusText: '澶勭悊涓�',
+          responsible: '鏉庝富绠�',
+          description: '涓庝緵搴斿晢B鐨勫悎鍚屽皢浜�2024骞�1鏈�25鏃ュ埌鏈熴��',
+          impact: '褰卞搷渚涘簲閾剧ǔ瀹氭�э紝鍙兘瀵艰嚧鏈嶅姟涓柇',
+          suggestions: '璇勪及渚涘簲鍟嗚〃鐜帮紝鍑嗗缁鏉愭枡锛屽埗瀹氬閫夋柟妗�'
+        },
+        {
+          id: 'W003',
+          title: '璁惧缁存姢棰勮',
+          type: '杩愯惀棰勮',
+          level: 'yellow',
+          levelText: '榛勮壊棰勮',
+          status: 'pending',
+          statusText: '寰呭鐞�',
+          responsible: '鐜嬪伐绋嬪笀',
+          description: '鐢熶骇绾胯澶嘋宸茶繍琛�8000灏忔椂锛屾帴杩戠淮鎶ゅ懆鏈熴��',
+          impact: '鍙兘褰卞搷鐢熶骇鏁堢巼鍜屼骇鍝佽川閲�',
+          suggestions: '瀹夋帓缁存姢鏃堕棿锛屽噯澶囧浠讹紝鍒跺畾缁存姢璁″垝'
+        },
+        {
+          id: 'W004',
+          title: '浜哄憳閰嶇疆棰勮',
+          type: '杩愯惀棰勮',
+          level: 'green',
+          levelText: '缁胯壊棰勮',
+          status: 'resolved',
+          statusText: '宸茶В鍐�',
+          responsible: '璧礖R',
+          description: '鎶�鏈儴闂ㄤ汉鍛橀厤缃厖瓒筹紝椤圭洰杩涘害姝e父銆�',
+          impact: '鏃犺礋闈㈠奖鍝�',
+          suggestions: '缁х画鐩戞帶浜哄憳閰嶇疆鎯呭喌'
+        },
+        {
+          id: 'W005',
+          title: '璐ㄩ噺浜嬫晠棰勮',
+          type: '杩愯惀棰勮',
+          level: 'red',
+          levelText: '绾㈣壊棰勮',
+          status: 'pending',
+          statusText: '寰呭鐞�',
+          responsible: '闄堟�荤洃',
+          description: '浜у搧D鍦ㄥ鎴风幇鍦哄嚭鐜拌川閲忛棶棰樸��',
+          impact: '褰卞搷瀹㈡埛婊℃剰搴︼紝鍙兘閫犳垚缁忔祹鎹熷け',
+          suggestions: '绔嬪嵆鍙洖闂浜у搧锛屽垎鏋愬師鍥狅紝鍒跺畾鏀硅繘鎺柦'
+        }
+      ]
+    }
+  },
+  methods: {
+    viewDetail(warning) {
+      this.currentWarning = warning
+      this.showDetail = true
+    }
+  }
+}
+</script>
+
+<style scoped>
+.warning-system {
+  padding: 20px;
+  max-width: 1200px;
+  margin: 0 auto;
+}
+
+h2 {
+  color: #333;
+  margin-bottom: 30px;
+}
+
+.stats {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+  gap: 20px;
+  margin-bottom: 30px;
+}
+
+.stat-card {
+  padding: 20px;
+  border-radius: 8px;
+  color: white;
+  text-align: center;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+}
+
+.stat-card.red { background: linear-gradient(135deg, #ff6b6b, #ee5a52); }
+.stat-card.orange { background: linear-gradient(135deg, #ffa726, #ff9800); }
+.stat-card.yellow { background: linear-gradient(135deg, #ffd54f, #ffc107); }
+.stat-card.green { background: linear-gradient(135deg, #66bb6a, #4caf50); }
+
+.stat-card .number {
+  display: block;
+  font-size: 32px;
+  font-weight: bold;
+  margin-bottom: 8px;
+}
+
+.stat-card .label {
+  font-size: 14px;
+  opacity: 0.9;
+}
+
+.warning-list h3 {
+  margin-bottom: 20px;
+  color: #333;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  background: white;
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+}
+
+th, td {
+  padding: 12px;
+  text-align: left;
+  border-bottom: 1px solid #eee;
+}
+
+th {
+  background: #f8f9fa;
+  font-weight: 600;
+  color: #333;
+}
+
+.level-tag, .status-tag {
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  color: white;
+}
+
+.level-tag.red { background: #f56c6c; }
+.level-tag.orange { background: #e6a23c; }
+.level-tag.yellow { background: #e6a23c; }
+.level-tag.green { background: #67c23a; }
+
+.status-tag.pending { background: #f56c6c; }
+.status-tag.processing { background: #e6a23c; }
+.status-tag.resolved { background: #67c23a; }
+
+button {
+  padding: 6px 12px;
+  margin: 0 4px;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 12px;
+  background: #409eff;
+  color: white;
+}
+
+.modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0,0,0,0.5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.modal-content {
+  background: white;
+  padding: 30px;
+  border-radius: 8px;
+  max-width: 600px;
+  width: 90%;
+  max-height: 80vh;
+  overflow-y: auto;
+}
+
+.modal-content h3 {
+  margin-bottom: 20px;
+  color: #333;
+}
+
+.modal-content p {
+  margin-bottom: 15px;
+  line-height: 1.6;
+}
+
+.modal-content strong {
+  color: #333;
+}
+
+.modal-content button {
+  background: #409eff;
+  color: white;
+  padding: 10px 20px;
+  font-size: 14px;
+}
+</style>
diff --git a/src/views/demo/fakePage/index.vue b/src/views/demo/fakePage/index.vue
new file mode 100644
index 0000000..42cef72
--- /dev/null
+++ b/src/views/demo/fakePage/index.vue
@@ -0,0 +1,248 @@
+<template>
+  <div class="app-container">
+    <el-card shadow="never">
+      <div class="toolbar">
+        <el-input
+          v-model="query.keyword"
+          placeholder="鎼滅储鍚嶇О/绫诲埆"
+          clearable
+          style="width: 240px"
+          @keyup.enter="handleSearch"
+        />
+        <el-select
+          v-model="query.status"
+          placeholder="鐘舵��"
+          clearable
+          style="width: 140px; margin-left: 12px"
+        >
+          <el-option label="鍚敤" value="鍚敤" />
+          <el-option label="鍋滅敤" value="鍋滅敤" />
+        </el-select>
+        <el-button type="primary" style="margin-left: 12px" @click="handleSearch">鏌ヨ</el-button>
+        <el-button @click="resetQuery">閲嶇疆</el-button>
+        <el-button type="success" plain style="float: right" @click="openCreate">鏂板</el-button>
+      </div>
+
+      <el-table :data="pagedList" border style="width: 100%" height="480">
+        <el-table-column prop="id" label="缂栧彿" width="90" sortable />
+        <el-table-column prop="name" label="鍚嶇О" min-width="140" />
+        <el-table-column prop="category" label="绫诲埆" width="120" />
+        <el-table-column prop="stock" label="搴撳瓨" width="100" sortable />
+        <el-table-column prop="price" label="鍗曚环(楼)" width="120">
+          <template #default="scope">{{ formatPrice(scope.row.price) }}</template>
+        </el-table-column>
+        <el-table-column label="鐘舵��" width="120">
+          <template #default="scope">
+            <el-tag :type="scope.row.status === '鍚敤' ? 'success' : 'info'">{{ scope.row.status }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="updatedAt" label="鏇存柊鏃堕棿" min-width="160" />
+        <el-table-column label="鎿嶄綔" width="180" fixed="right">
+          <template #default="scope">
+            <el-button link type="primary" @click="openEdit(scope.row)">缂栬緫</el-button>
+            <el-button link type="danger" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="pagination">
+        <el-pagination
+          background
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="filteredList.length"
+          :page-sizes="[5, 10, 20, 50]"
+          :page-size="pager.pageSize"
+          :current-page="pager.pageNum"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+
+    <el-dialog v-model="dialogVisible" :title="isEdit ? '缂栬緫' : '鏂板'" width="520px">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="90px">
+        <el-form-item label="鍚嶇О" prop="name">
+          <el-input v-model="form.name" placeholder="璇疯緭鍏ュ悕绉�" />
+        </el-form-item>
+        <el-form-item label="绫诲埆" prop="category">
+          <el-select v-model="form.category" placeholder="璇烽�夋嫨绫诲埆" style="width: 100%">
+            <el-option label="鍘熸枡" value="鍘熸枡" />
+            <el-option label="鍗婃垚鍝�" value="鍗婃垚鍝�" />
+            <el-option label="鎴愬搧" value="鎴愬搧" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="搴撳瓨" prop="stock">
+          <el-input v-model.number="form.stock" type="number" min="0" />
+        </el-form-item>
+        <el-form-item label="鍗曚环(楼)" prop="price">
+          <el-input v-model.number="form.price" type="number" min="0" step="0.01" />
+        </el-form-item>
+        <el-form-item label="鐘舵��" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio label="鍚敤">鍚敤</el-radio>
+            <el-radio label="鍋滅敤">鍋滅敤</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+  
+</template>
+
+<script setup>
+import { ref, reactive, computed, nextTick } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+
+defineOptions({ name: 'FakePage' })
+
+const query = reactive({
+  keyword: '',
+  status: ''
+})
+
+const pager = reactive({
+  pageNum: 1,
+  pageSize: 10
+})
+
+const allList = ref(generateMockData())
+
+const filteredList = computed(() => {
+  const keyword = (query.keyword || '').trim()
+  const status = query.status
+  return allList.value.filter(item => {
+    const hitKeyword = !keyword || item.name.includes(keyword) || item.category.includes(keyword)
+    const hitStatus = !status || item.status === status
+    return hitKeyword && hitStatus
+  })
+})
+
+const pagedList = computed(() => {
+  const start = (pager.pageNum - 1) * pager.pageSize
+  const end = start + pager.pageSize
+  return filteredList.value.slice(start, end)
+})
+
+function handleSearch() {
+  pager.pageNum = 1
+}
+
+function resetQuery() {
+  query.keyword = ''
+  query.status = ''
+  pager.pageNum = 1
+}
+
+function handleSizeChange(size) {
+  pager.pageSize = size
+  pager.pageNum = 1
+}
+
+function handleCurrentChange(page) {
+  pager.pageNum = page
+}
+
+function formatPrice(val) {
+  return Number(val || 0).toFixed(2)
+}
+
+// 鏂板/缂栬緫
+const dialogVisible = ref(false)
+const isEdit = ref(false)
+const formRef = ref()
+const form = reactive({ id: null, name: '', category: '', stock: 0, price: 0, status: '鍚敤' })
+
+const rules = {
+  name: [{ required: true, message: '璇疯緭鍏ュ悕绉�', trigger: 'blur' }],
+  category: [{ required: true, message: '璇烽�夋嫨绫诲埆', trigger: 'change' }],
+  stock: [{ required: true, message: '璇疯緭鍏ュ簱瀛�', trigger: 'blur' }],
+  price: [{ required: true, message: '璇疯緭鍏ュ崟浠�', trigger: 'blur' }]
+}
+
+function openCreate() {
+  isEdit.value = false
+  Object.assign(form, { id: null, name: '', category: '', stock: 0, price: 0, status: '鍚敤' })
+  dialogVisible.value = true
+  nextTick(() => formRef.value?.clearValidate?.())
+}
+
+function openEdit(row) {
+  isEdit.value = true
+  Object.assign(form, JSON.parse(JSON.stringify(row)))
+  dialogVisible.value = true
+  nextTick(() => formRef.value?.clearValidate?.())
+}
+
+function submitForm() {
+  formRef.value?.validate?.((valid) => {
+    if (!valid) return
+    if (isEdit.value) {
+      const index = allList.value.findIndex(x => x.id === form.id)
+      if (index > -1) {
+        allList.value[index] = { ...form, updatedAt: nowString() }
+        ElMessage.success('宸蹭繚瀛�')
+      }
+    } else {
+      const newId = Date.now()
+      allList.value.unshift({ ...form, id: newId, updatedAt: nowString() })
+      ElMessage.success('宸叉柊澧�')
+    }
+    dialogVisible.value = false
+  })
+}
+
+function handleDelete(row) {
+  ElMessageBox.confirm(`纭鍒犻櫎銆�${row.name}銆戝悧锛焋, '鎻愮ず', { type: 'warning' })
+    .then(() => {
+      allList.value = allList.value.filter(x => x.id !== row.id)
+      ElMessage.success('宸插垹闄�')
+    })
+    .catch(() => {})
+}
+
+function generateMockData() {
+  const categories = ['鍘熸枡', '鍗婃垚鍝�', '鎴愬搧']
+  const statusOptions = ['鍚敤', '鍋滅敤']
+  const list = []
+  for (let i = 1; i <= 36; i++) {
+    list.push({
+      id: i,
+      name: `鐗╂枡-${i.toString().padStart(3, '0')}`,
+      category: categories[i % categories.length],
+      stock: Math.floor(Math.random() * 1000),
+      price: (Math.random() * 500 + 10).toFixed(2),
+      status: statusOptions[i % 2],
+      updatedAt: nowString()
+    })
+  }
+  return list
+}
+
+function nowString() {
+  const d = new Date()
+  const yyyy = d.getFullYear()
+  const MM = String(d.getMonth() + 1).padStart(2, '0')
+  const dd = String(d.getDate()).padStart(2, '0')
+  const hh = String(d.getHours()).padStart(2, '0')
+  const mm = String(d.getMinutes()).padStart(2, '0')
+  const ss = String(d.getSeconds()).padStart(2, '0')
+  return `${yyyy}-${MM}-${dd} ${hh}:${mm}:${ss}`
+}
+</script>
+
+<style scoped>
+.toolbar {
+  margin-bottom: 12px;
+}
+.pagination {
+  margin-top: 12px;
+  text-align: right;
+}
+</style>
+
+
+

--
Gitblit v1.9.3