Ver Fonte

首页修改

bzd_lxf há 1 mês atrás
pai
commit
0248b74743
1 ficheiros alterados com 1538 adições e 124 exclusões
  1. 1538 124
      pm_ui/src/views/index.vue

+ 1538 - 124
pm_ui/src/views/index.vue

@@ -1,172 +1,1586 @@
 <template>
-  <div class="app-container home">
-    <el-row :gutter="20">
-      <!-- 左侧内容 -->
-      <el-col :sm="24" :lg="12" style="padding-left: 20px">
-        <h2>IBMS后台管理框架</h2>
-        <p>
-          <b>当前版本:</b> <span>v{{ version }}</span>
-        </p>
-      </el-col>
+  <div class="dashboard-container">
+    <!-- 顶部欢迎区域 -->
+    <div class="welcome-section">
+      <div class="welcome-content">
+        <h1 class="welcome-title">
+          <span class="gradient-text">IBMS智能建筑管理系统</span>
+        </h1>
+        <p class="welcome-subtitle">集成化、智能化、可视化的建筑管理解决方案</p>
+        <div class="version-info">
+          <el-tag type="primary" effect="dark" @click="showVersionInfo">
+            <i class="el-icon-price-tag"></i> v{{ version }}
+          </el-tag>
+          <el-tag type="success" effect="plain" @click="showSystemStatus">
+            <i class="el-icon-circle-check"></i> 运行正常
+          </el-tag>
+        </div>
+      </div>
+      <div class="welcome-illustration">
+        <!-- SVG 图形保持不变 -->
+        <svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">
+          <defs>
+            <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
+              <stop offset="0%" style="stop-color:#667eea;stop-opacity:0.8" />
+              <stop offset="100%" style="stop-color:#764ba2;stop-opacity:0.8" />
+            </linearGradient>
+          </defs>
+          <!-- 建筑轮廓 -->
+          <rect x="50" y="100" width="80" height="150" fill="rgba(255,255,255,0.2)" stroke="white" stroke-width="2" rx="5"/>
+          <rect x="150" y="50" width="100" height="200" fill="rgba(255,255,255,0.2)" stroke="white" stroke-width="2" rx="5"/>
+          <rect x="270" y="120" width="80" height="130" fill="rgba(255,255,255,0.2)" stroke="white" stroke-width="2" rx="5"/>
+
+          <!-- 窗户 -->
+          <rect x="60" y="110" width="20" height="20" fill="rgba(255,255,255,0.5)" />
+          <rect x="90" y="110" width="20" height="20" fill="rgba(255,255,255,0.5)" />
+          <rect x="60" y="140" width="20" height="20" fill="rgba(255,255,255,0.5)" />
+          <rect x="90" y="140" width="20" height="20" fill="rgba(255,255,255,0.5)" />
+
+          <!-- 连接线 -->
+          <path d="M 90 250 Q 200 280 310 250" stroke="white" stroke-width="2" fill="none" stroke-dasharray="5,5" opacity="0.5"/>
+          <circle cx="200" cy="265" r="5" fill="white" opacity="0.8"/>
 
-      <!-- 右侧技术选型 -->
-      <el-col :sm="24" :lg="12" style="padding-left: 50px">
-        <el-row>
-          <el-col :span="12">
-            <h2>技术选型</h2>
-          </el-col>
-        </el-row>
-        <el-row>
-          <el-col :span="6">
-            <h4>后端技术</h4>
-            <ul>
-              <li v-for="(item, index) in backendTech" :key="index">{{ item }}</li>
-            </ul>
-          </el-col>
-          <el-col :span="6">
-            <h4>前端技术</h4>
-            <ul>
-              <li v-for="(item, index) in frontendTech" :key="index">{{ item }}</li>
-            </ul>
-          </el-col>
-        </el-row>
+          <!-- 图标 -->
+          <text x="200" y="40" text-anchor="middle" fill="white" font-size="24" font-weight="bold">IBMS</text>
+        </svg>
+      </div>
+    </div>
+
+    <!-- 快速统计卡片 -->
+    <el-row :gutter="20" class="stat-cards">
+      <el-col :xs="24" :sm="12" :md="6" v-for="(stat, index) in statistics" :key="index">
+        <div class="stat-card" :style="{ background: stat.color }" @click="handleStatClick(stat)">
+          <div class="stat-icon">
+            <component :is="stat.iconComponent" />
+          </div>
+          <div class="stat-content">
+            <div class="stat-value">
+              <span class="count-up">{{ stat.value }}</span>
+            </div>
+            <div class="stat-label">{{ stat.label }}</div>
+          </div>
+          <div class="stat-trend" :class="stat.trend > 0 ? 'up' : 'down'">
+            <el-icon>
+              <ArrowUp v-if="stat.trend > 0" />
+              <ArrowDown v-else />
+            </el-icon>
+            {{ Math.abs(stat.trend) }}%
+          </div>
+        </div>
       </el-col>
     </el-row>
 
-    <el-divider />
+    <!-- 主要内容区域 -->
+    <el-row :gutter="20" class="main-content">
+      <!-- 系统功能模块 -->
+      <el-col :xs="24" :lg="16">
+        <el-card class="feature-card">
+          <template #header>
+            <div class="card-header">
+              <h3>
+                <el-icon><Menu /></el-icon>
+                系统功能模块
+              </h3>
+              <el-button type="text" @click="viewAllModules">
+                查看全部
+                <el-icon><ArrowRight /></el-icon>
+              </el-button>
+            </div>
+          </template>
+          <el-row :gutter="15">
+            <el-col :xs="12" :sm="8" :md="6" v-for="(module, index) in systemModules" :key="index">
+              <div class="module-item" @click="handleModuleClick(module)">
+                <div class="module-icon" :style="{ background: module.color }">
+                  <component :is="module.iconComponent" />
+                </div>
+                <div class="module-name">{{ module.name }}</div>
+                <div class="module-desc">{{ module.desc }}</div>
+              </div>
+            </el-col>
+          </el-row>
+        </el-card>
+
+        <!-- 实时监控数据 -->
+        <!-- 实时监控数据 -->
+        <el-card class="monitor-card">
+          <template #header>
+            <div class="card-header">
+              <h3>
+                <el-icon><DataLine /></el-icon>
+                实时监控数据
+              </h3>
+              <el-button-group>
+                <el-button size="small" :type="chartType === 'line' ? 'primary' : ''" @click="switchChart('line')">
+                  折线图
+                </el-button>
+                <el-button size="small" :type="chartType === 'bar' ? 'primary' : ''" @click="switchChart('bar')">
+                  柱状图
+                </el-button>
+              </el-button-group>
+            </div>
+          </template>
+          <div class="chart-container" @click="viewDetailChart">
+            <!-- 模拟图表 -->
+            <div class="mock-chart">
+              <div class="chart-bars" v-if="chartType === 'bar'">
+                <div
+                    v-for="(bar, index) in mockChartData"
+                    :key="index"
+                    class="bar"
+                    :style="{ height: bar + '%', animationDelay: index * 0.1 + 's' }"
+                    @click.stop="showDataDetail(index, bar)"
+                >
+                  <span class="bar-value">{{ bar }}%</span>
+                </div>
+              </div>
+              <div class="chart-lines" v-else>
+                <!-- 简单的折线图模拟 -->
+                <svg width="100%" height="100%" :viewBox="`0 0 ${chartWidth} ${chartHeight}`" preserveAspectRatio="none">
+                  <!-- 网格线 -->
+                  <g class="grid">
+                    <!-- 水平网格线 -->
+                    <line v-for="i in 5" :key="'h-' + i"
+                          :x1="0"
+                          :y1="(chartHeight / 5) * i"
+                          :x2="chartWidth"
+                          :y2="(chartHeight / 5) * i"
+                          stroke="#e0e0e0"
+                          stroke-width="1" />
+                    <!-- 垂直网格线 -->
+                    <line v-for="(label, index) in chartLabels" :key="'v-' + index"
+                          :x1="getXPosition(index)"
+                          :y1="0"
+                          :x2="getXPosition(index)"
+                          :y2="chartHeight"
+                          stroke="#e0e0e0"
+                          stroke-width="1" />
+                  </g>
 
-    <!-- 其他内容 -->
-    <el-row :gutter="20">
-      <el-col :span="24">
-        <h2>更新日志</h2>
-        <ol class="update-log">
-          <li v-for="(log, index) in updateLogs" :key="index">{{ log }}</li>
-        </ol>
+                  <!-- 折线 -->
+                  <polyline
+                      :points="linePoints"
+                      fill="none"
+                      stroke="#409EFF"
+                      stroke-width="3"
+                      stroke-linejoin="round"
+                      stroke-linecap="round"
+                  />
+
+                  <!-- 数据点 -->
+                  <g>
+                    <circle
+                        v-for="(point, index) in linePointsArray"
+                        :key="index"
+                        :cx="point.x"
+                        :cy="point.y"
+                        r="5"
+                        fill="#409EFF"
+                        stroke="white"
+                        stroke-width="2"
+                        @click.stop="showDataDetail(index, mockChartData[index])"
+                        style="cursor: pointer;"
+                        class="data-point"
+                    >
+                      <title>{{ chartLabels[index] }}: {{ mockChartData[index] }}%</title>
+                    </circle>
+                  </g>
+
+                  <!-- 数值标签 -->
+                  <g>
+                    <text
+                        v-for="(point, index) in linePointsArray"
+                        :key="'text-' + index"
+                        :x="point.x"
+                        :y="point.y - 10"
+                        text-anchor="middle"
+                        font-size="12"
+                        fill="#606266"
+                        class="value-label"
+                    >
+                      {{ mockChartData[index] }}%
+                    </text>
+                  </g>
+                </svg>
+              </div>
+              <div class="chart-labels">
+                <span v-for="(label, index) in chartLabels" :key="index" @click="showLabelDetail(label)">{{ label }}</span>
+              </div>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+
+      <!-- 右侧信息栏 -->
+      <el-col :xs="24" :lg="8">
+        <!-- 技术栈 -->
+        <el-card class="tech-card">
+          <template #header>
+            <div class="card-header">
+              <h3>
+                <el-icon><Cpu /></el-icon>
+                技术架构
+              </h3>
+            </div>
+          </template>
+          <el-tabs v-model="activeTech" class="tech-tabs">
+            <el-tab-pane label="后端技术" name="backend">
+              <div class="tech-list">
+                <div class="tech-item" v-for="(tech, index) in backendTech" :key="index" @click="showTechDetail(tech)">
+                  <el-tag :type="tech.type" effect="plain">{{ tech.name }}</el-tag>
+                  <span class="tech-version">{{ tech.version }}</span>
+                </div>
+              </div>
+            </el-tab-pane>
+            <el-tab-pane label="前端技术" name="frontend">
+              <div class="tech-list">
+                <div class="tech-item" v-for="(tech, index) in frontendTech" :key="index" @click="showTechDetail(tech)">
+                  <el-tag :type="tech.type" effect="plain">{{ tech.name }}</el-tag>
+                  <span class="tech-version">{{ tech.version }}</span>
+                </div>
+              </div>
+            </el-tab-pane>
+          </el-tabs>
+        </el-card>
+
+        <!-- 系统公告 -->
+        <el-card class="notice-card">
+          <template #header>
+            <div class="card-header">
+              <h3>
+                <el-icon><Bell /></el-icon>
+                系统公告
+<!--                <el-badge v-if="unreadCount > 0" :value="unreadCount" :max="99" class="notice-badge" />-->
+              </h3>
+              <el-link type="primary" @click="viewAllNotices">更多</el-link>
+            </div>
+          </template>
+          <el-skeleton :loading="noticeLoading" animated :count="3">
+            <template #template>
+              <el-skeleton-item variant="h3" style="width: 50%; margin-bottom: 10px;" />
+              <el-skeleton-item variant="text" style="margin-bottom: 5px;" />
+              <el-skeleton-item variant="text" style="width: 80%; margin-bottom: 20px;" />
+            </template>
+            <template #default>
+              <el-timeline v-if="notices.length > 0">
+                <el-timeline-item
+                    v-for="(notice, index) in notices"
+                    :key="notice.notice_id"
+                    :timestamp="parseTime(notice.create_time)"
+                    :type="getNoticeTimelineType(notice.notice_type)"
+                    placement="top">
+                  <div class="notice-content"><!-- @click="viewNoticeDetail(notice)-->
+                    <h4>
+                      {{ notice.notice_title }}
+<!--                      <el-tag v-if="!notice.is_read" type="danger" size="small" effect="plain">未读</el-tag>-->
+                    </h4>
+                    <p>{{ getNoticePreview(notice.notice_content) }}</p>
+                    <div class="notice-meta">
+                    <span class="notice-type">
+                      <dict-tag :options="sys_notice_type" :value="notice.notice_type" />
+                    </span>
+                      <span class="notice-author">{{ notice.create_by }}</span>
+                    </div>
+                  </div>
+                </el-timeline-item>
+              </el-timeline>
+              <el-empty v-else description="暂无公告" />
+            </template>
+          </el-skeleton>
+        </el-card>
+
+        <!-- 快捷操作 -->
+        <el-card class="quick-action-card">
+          <template #header>
+            <div class="card-header">
+              <h3>
+                <el-icon><Lightning /></el-icon>
+                快捷操作
+              </h3>
+            </div>
+          </template>
+          <div class="quick-actions">
+            <el-button
+                v-for="(action, index) in quickActions"
+                :key="index"
+                :type="action.type"
+                @click="handleQuickAction(action)">
+              <el-icon>
+                <component :is="action.iconComponent" />
+              </el-icon>
+              {{ action.name }}
+            </el-button>
+          </div>
+        </el-card>
       </el-col>
     </el-row>
   </div>
 </template>
 
-<script setup name="Index">
-import { ref } from 'vue';
+<script setup name="Dashboard">
+import {
+  Monitor,
+  Connection,
+  Lightning,
+  Warning,
+  Menu,
+  ArrowRight,
+  DataLine,
+  Cpu,
+  Bell,
+  ArrowUp,
+  ArrowDown,
+  VideoCamera,
+  Search,
+  Document,
+  Setting,
+  Download,
+  PartlyCloudy
+} from '@element-plus/icons-vue';
+// 导入必要的API和工具
+import { ref, reactive, onMounted, onUnmounted, shallowRef, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { ElMessage, ElMessageBox, ElNotification } from 'element-plus';
+import { getNotice, show as markNoticeAsRead } from "@/api/system/notice";
+import {listNoticeRead} from "@/api/system/menu";
+import { connectToWebSocket } from "@/layout/components/websocket";
 
+// 获取字典数据
+const { proxy } = getCurrentInstance();
+const { sys_notice_status, sys_notice_type } = proxy.useDict("sys_notice_status", "sys_notice_type");
+
+const router = useRouter();
 const version = ref('3.8.9');
+const activeTech = ref('backend');
+const chartType = ref('bar');
 
-// 后端技术列表
-const backendTech = [
-  'SpringBoot',
-  'Spring Security',
-  'JWT',
-  'MyBatis',
-  'Druid',
-  'Fastjson',
-  '...'
-];
-
-// 前端技术列表
-const frontendTech = [
-  'Vue',
-  'Vuex',
-  'Element-ui',
-  'Axios',
-  'Sass',
-  'Quill',
-  '...'
-];
-
-// 更新日志
-const updateLogs = [
-  '修复了一些已知问题',
-  '优化了性能',
-  '新增了用户管理模块',
-  '...'
-];
-</script>
+// 系统公告相关的响应式数据
+const notices = ref([]);
+const noticeLoading = ref(false);
+const unreadCount = ref(0);
+const noticeDialogVisible = ref(false);
+const currentNotice = ref({});
 
-<style scoped lang="scss">
-.home {
-  background: #f0f4f8;
-  color: #333;
-  font-family: 'Roboto', sans-serif;
-  min-height: auto;
-  padding: 20px;
+// WebSocket连接
+let wsConnection = null;
+
+// 查询参数
+const noticeQueryParams = ref({
+  pageNum: 1,
+  pageSize: 5,  // Dashboard只显示最新5条
+  orderByColumn: 'create_time',
+  isAsc: 'desc'
+});
 
-  h2 {
-    font-size: 26px;
-    font-weight: bold;
-    margin-bottom: 15px;
+// 获取公告列表
+const getNoticeList = async () => {
+  noticeLoading.value = true;
+  try {
+    const response = await listNoticeRead(noticeQueryParams.value);
+    if (response.code === 200) {
+      // 获取前5条公告
+      notices.value = response.data.slice(0, 3);
+      // 计算未读数量
+      unreadCount.value = response.data.filter(item => !item.is_read).length;
+    }
+  } catch (error) {
+    console.error('获取公告列表失败:', error);
+    ElMessage.error('获取公告列表失败');
+  } finally {
+    noticeLoading.value = false;
   }
+};
+
+// 获取公告预览内容(去除HTML标签,限制长度)
+const getNoticePreview = (content) => {
+  if (!content) return '';
+  // 去除HTML标签
+  const text = content.replace(/<[^>]*>/g, '');
+  // 限制长度
+  return text.length > 50 ? text.substring(0, 50) + '...' : text;
+};
 
-  p {
-    font-size: 16px;
-    margin-top: 10px;
+// 根据公告类型返回时间线类型
+const getNoticeTimelineType = (noticeType) => {
+  const typeMap = {
+    '1': 'primary',   // 通知
+    '2': 'success',   // 公告
+    '3': 'warning',   // 提醒
+    '4': 'danger'     // 紧急
+  };
+  return typeMap[noticeType] || 'info';
+};
 
-    b {
-      color: #409eff;
-      font-weight: bold;
+// 查看公告详情
+/*const viewNoticeDetail = async (notice) => {
+  try {
+    const response = await getNotice(notice.notice_id);
+    if (response.code === 200) {
+      currentNotice.value = response.data;
+
+      ElMessageBox.alert(
+          `<div style="text-align: left;">
+          <p><strong>公告标题:</strong>${response.data.noticeTitle}</p>
+          <p><strong>公告类型:</strong>${getNoticeTypeLabel(response.data.noticeType)}</p>
+          <p><strong>发布人:</strong>${response.data.createBy}</p>
+          <p><strong>发布时间:</strong>${response.data.createTime}</p>
+          <p><strong>公告内容:</strong></p>
+          <div style="margin-top: 10px; padding: 10px; background: #f5f7fa; border-radius: 4px;">
+            ${response.data.noticeContent}
+          </div>
+        </div>`,
+          '公告详情',
+          {
+            dangerouslyUseHTMLString: true,
+            confirmButtonText: '已读',
+            customClass: 'notice-detail-dialog',
+            beforeClose: async (action, instance, done) => {
+              if (action === 'confirm' && !notice.is_read) {
+                await markAsRead(notice.notice_id);
+              }
+              done();
+            }
+          }
+      );
     }
+  } catch (error) {
+    console.error('获取公告详情失败:', error);
+    ElMessage.error('获取公告详情失败');
   }
+};*/
 
-  .el-row {
-    margin-bottom: 20px;
+// 标记为已读
+const markAsRead = async (noticeId) => {
+  try {
+    const response = await markNoticeAsRead({ noticeId });
+    if (response.code === 200) {
+      // 刷新公告列表
+      await getNoticeList();
+      ElMessage.success('已标记为已读');
+    }
+  } catch (error) {
+    console.error('标记已读失败:', error);
   }
+};
 
-  .el-col {
-    background: #ffffff;
-    border-radius: 10px;
-    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
-    padding: 20px;
-    transition: transform 0.3s ease;
+// 获取公告类型标签
+const getNoticeTypeLabel = (type) => {
+  const dict = sys_notice_type.value.find(item => item.value === type);
+  return dict ? dict.label : type;
+};
+
+// 查看所有公告
+const viewAllNotices = () => {
+  router.push('/system/notice');
+};
+
+// WebSocket消息处理
+const handleWebSocketMessage = (message) => {
+  if (message && message !== "del" && message !== "update") {
+    ElNotification({
+      title: '新公告',
+      message: '您有新的公告,请注意查收',
+      type: 'warning',
+      duration: 5000,
+      position: 'top-right',
+      onClick: () => {
+        getNoticeList();
+      }
+    });
+  }
+  // 刷新公告列表
+  getNoticeList();
+};
+
+// 其他原有的方法保持不变...
+
+onMounted(() => {
+  // 初始化时显示欢迎信息
+  ElMessage.success('欢迎使用IBMS智能建筑管理系统!');
+
+  // 获取公告列表
+  getNoticeList();
+
+  // 连接WebSocket
+  wsConnection = connectToWebSocket(handleWebSocketMessage);
+
+  // 其他原有的初始化代码...
+});
+
+onUnmounted(() => {
+  // 清理WebSocket连接
+  if (wsConnection) {
+    // 根据实际的WebSocket实现进行清理
+  }
+});
+
+// 图表尺寸
+const chartWidth = ref(600);
+const chartHeight = ref(250);
+
+// 获取X轴位置
+const getXPosition = (index) => {
+  const padding = 40;
+  const step = (chartWidth.value - 2 * padding) / (chartLabels.length - 1);
+  return padding + index * step;
+};
+
+// 计算折线图点位
+const linePoints = computed(() => {
+  const padding = 40;
+  const xStep = (chartWidth.value - 2 * padding) / (mockChartData.value.length - 1);
+  const yRange = chartHeight.value - 2 * padding;
+
+  return mockChartData.value.map((value, index) => {
+    const x = padding + index * xStep;
+    const y = chartHeight.value - padding - (value / 100) * yRange;
+    return `${x},${y}`;
+  }).join(' ');
+});
+
+const linePointsArray = computed(() => {
+  const padding = 40;
+  const xStep = (chartWidth.value - 2 * padding) / (mockChartData.value.length - 1);
+  const yRange = chartHeight.value - 2 * padding;
+
+  return mockChartData.value.map((value, index) => ({
+    x: padding + index * xStep,
+    y: chartHeight.value - padding - (value / 100) * yRange
+  }));
+});
+
+// 模拟图表数据
+const mockChartData = ref([65, 78, 90, 70, 85, 60, 75]);
+const chartLabels = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
+
+// 统计数据
+const statistics = reactive([
+  {
+    label: '设备总数',
+    value: '1,234',
+    iconComponent: shallowRef(Monitor),
+    color: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+    trend: 12.5,
+    path: '/device/list'
+  },
+  {
+    label: '在线设备',
+    value: '1,180',
+    iconComponent: shallowRef(Connection),
+    color: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
+    trend: 8.3,
+    path: '/device/online'
+  },
+  {
+    label: '今日能耗',
+    value: '2,845',
+    iconComponent: shallowRef(Lightning),
+    color: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
+    trend: -5.2,
+    path: '/energy/today'
+  },
+  {
+    label: '告警数量',
+    value: '12',
+    iconComponent: shallowRef(Warning),
+    color: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)',
+    trend: -15.8,
+    path: '/alarm/list'
+  }
+]);
+
+// 系统模块
+const systemModules = reactive([
+  {
+    name: '能源管理',
+    desc: '能耗监控分析',
+    iconComponent: shallowRef(Lightning),
+    color: '#409EFF',
+    path: '/energy'
+  },
+  {
+    name: '设备监控',
+    desc: '设备状态管理',
+    iconComponent: shallowRef(Monitor),
+    color: '#67C23A',
+    path: '/device'
+  },
+  {
+    name: '环境监测',
+    desc: '环境参数监控',
+    iconComponent: shallowRef(PartlyCloudy),
+    color: '#E6A23C',
+    path: '/environment'
+  },
+  {
+    name: '安防系统',
+    desc: '安全防护管理',
+    iconComponent: shallowRef(VideoCamera),
+    color: '#F56C6C',
+    path: '/security'
+  },
+  {
+    name: '照明控制',
+    desc: '智能照明管理',
+    iconComponent: shallowRef(Lightning),
+    color: '#909399',
+    path: '/lighting'
+  },
+  {
+    name: '报警管理',
+    desc: '告警处理中心',
+    iconComponent: shallowRef(Bell),
+    color: '#E6A23C',
+    path: '/alarm'
+  }
+]);
+
+// 技术栈数据
+const backendTech = reactive([
+  { name: 'Spring Boot', version: '2.7.x', type: 'primary', desc: '基础框架' },
+  { name: 'Spring Security', version: '5.7.x', type: 'success', desc: '安全框架' },
+  { name: 'JWT', version: '0.11.x', type: 'info', desc: '认证方案' },
+  { name: 'MyBatis Plus', version: '3.5.x', type: 'warning', desc: 'ORM框架' },
+  { name: 'Redis', version: '6.2.x', type: 'danger', desc: '缓存中间件' },
+  { name: 'MySQL', version: '8.0.x', type: 'primary', desc: '关系型数据库' },
+  { name: 'RabbitMQ', version: '3.9.x', type: 'success', desc: '消息队列' }
+]);
+
+const frontendTech = reactive([
+  { name: 'Vue 3', version: '3.3.x', type: 'primary', desc: '前端框架' },
+  { name: 'Vite', version: '4.4.x', type: 'success', desc: '构建工具' },
+  { name: 'Element Plus', version: '2.3.x', type: 'info', desc: 'UI组件库' },
+  { name: 'Pinia', version: '2.1.x', type: 'warning', desc: '状态管理' },
+  { name: 'Axios', version: '1.4.x', type: 'danger', desc: 'HTTP客户端' },
+  { name: 'ECharts', version: '5.4.x', type: 'primary', desc: '图表库' },
+  { name: 'TypeScript', version: '5.1.x', type: 'success', desc: '类型系统' }
+]);
+
+
+// 快捷操作
+const quickActions = reactive([
+  {
+    name: '设备巡检',
+    iconComponent: shallowRef(Search),
+    type: 'primary',
+    action: 'inspection'
+  },
+  {
+    name: '生成报表',
+    iconComponent: shallowRef(Document),
+    type: 'success',
+    action: 'report'
+  },
+  {
+    name: '系统设置',
+    iconComponent: shallowRef(Setting),
+    type: 'info',
+    action: 'settings'
+  },
+  {
+    name: '数据备份',
+    iconComponent: shallowRef(Download),
+    type: 'warning',
+    action: 'backup'
+  }
+]);
+
+// 显示版本信息
+const showVersionInfo = () => {
+  ElMessageBox.alert(
+      `<div style="text-align: left;">
+      <p><strong>当前版本:</strong>v${version.value}</p>
+      <p><strong>发布日期:</strong>2024-01-15</p>
+      <p><strong>更新内容:</strong></p>
+      <ul style="margin-left: 20px;">
+        <li>优化系统性能</li>
+        <li>新增能源分析报表</li>
+        <li>修复已知问题</li>
+      </ul>
+    </div>`,
+      '版本信息',
+      {
+        dangerouslyUseHTMLString: true,
+        confirmButtonText: '确定'
+      }
+  );
+};
+
+// 显示系统状态
+const showSystemStatus = () => {
+  ElMessageBox.alert(
+      `<div style="text-align: left;">
+      <p><strong>系统状态:</strong><span style="color: #67C23A;">运行正常</span></p>
+      <p><strong>运行时长:</strong>15天 8小时 32分钟</p>
+      <p><strong>CPU使用率:</strong>35%</p>
+      <p><strong>内存使用率:</strong>62%</p>
+      <p><strong>磁盘使用率:</strong>48%</p>
+    </div>`,
+      '系统状态',
+      {
+        dangerouslyUseHTMLString: true,
+        confirmButtonText: '确定'
+      }
+  );
+};
+
+// 处理统计卡片点击
+const handleStatClick = (stat) => {
+  ElMessage.info(`正在查看${stat.label}详情...`);
+  // 实际项目中这里应该跳转到对应页面
+  // router.push(stat.path);
+};
+
+// 查看所有模块
+const viewAllModules = () => {
+  ElMessage.success('正在加载所有系统模块...');
+  // router.push('/modules');
+};
+
+// 处理模块点击
+const handleModuleClick = (module) => {
+  ElMessage.success(`正在进入${module.name}模块`);
+  // 实际项目中这里应该跳转到对应页面
+  // router.push(module.path);
+};
+
+// 切换图表类型
+const switchChart = (type) => {
+  chartType.value = type;
+  ElMessage.success(`已切换为${type === 'line' ? '折线图' : '柱状图'}`);
+  // 这里可以重新加载图表数据
+  refreshChartData();
+};
+
+// 刷新图表数据
+const refreshChartData = () => {
+  // 模拟数据刷新
+  mockChartData.value = mockChartData.value.map(() =>
+      Math.floor(Math.random() * 40) + 60
+  );
+};
+
+// 查看详细图表
+const viewDetailChart = () => {
+  ElMessage.info('正在打开详细数据分析页面...');
+  // router.push('/analysis/chart');
+};
+
+// 显示数据详情
+const showDataDetail = (index, value) => {
+  ElMessageBox.alert(
+      `<div style="text-align: left;">
+      <p><strong>日期:</strong>${chartLabels[index]}</p>
+      <p><strong>数值:</strong>${value}%</p>
+      <p><strong>环比:</strong>${index > 0 ? ((value - mockChartData.value[index - 1]) > 0 ? '+' : '') + (value - mockChartData.value[index - 1]).toFixed(1) + '%' : '无'}</p>
+      <p><strong>状态:</strong>${value > 80 ? '<span style="color: #F56C6C;">偏高</span>' : value > 60 ? '<span style="color: #E6A23C;">正常</span>' : '<span style="color: #67C23A;">偏低</span>'}</p>
+    </div>`,
+      '数据详情',
+      {
+        dangerouslyUseHTMLString: true,
+        confirmButtonText: '确定'
+      }
+  );
+};
+
+// 显示标签详情
+const showLabelDetail = (label) => {
+  ElMessage.info(`查看${label}的详细数据`);
+};
+
+// 显示技术详情
+const showTechDetail = (tech) => {
+  ElMessageBox.alert(
+      `<div style="text-align: left;">
+      <p><strong>技术名称:</strong>${tech.name}</p>
+      <p><strong>当前版本:</strong>${tech.version}</p>
+      <p><strong>功能描述:</strong>${tech.desc}</p>
+      <p><strong>官方网站:</strong><a href="#" style="color: #409EFF;">查看官网</a></p>
+    </div>`,
+      '技术详情',
+      {
+        dangerouslyUseHTMLString: true,
+        confirmButtonText: '确定'
+      }
+  );
+};
+
+// 处理快捷操作
+const handleQuickAction = (action) => {
+  switch (action.action) {
+    case 'inspection':
+      ElMessageBox.confirm('是否立即开始设备巡检?', '设备巡检', {
+        confirmButtonText: '开始巡检',
+        cancelButtonText: '取消',
+        type: 'info'
+      }).then(() => {
+        ElMessage.success('设备巡检已开始,预计需要5分钟');
+        // 这里可以调用巡检API
+      }).catch(() => {
+        ElMessage.info('已取消设备巡检');
+      });
+      break;
+
+    case 'report':
+      ElMessageBox.prompt('请选择报表类型', '生成报表', {
+        confirmButtonText: '生成',
+        cancelButtonText: '取消',
+        inputPattern: /^(日报|周报|月报)$/,
+        inputPlaceholder: '请输入:日报、周报或月报',
+        inputErrorMessage: '请输入正确的报表类型'
+      }).then(({ value }) => {
+        ElMessage.success(`正在生成${value},请稍候...`);
+        // 模拟生成报表
+        setTimeout(() => {
+          ElMessage.success(`${value}生成成功,已保存到系统`);
+        }, 2000);
+      }).catch(() => {
+        ElMessage.info('已取消生成报表');
+      });
+      break;
+
+    case 'settings':
+      ElMessage.info('正在打开系统设置...');
+      // router.push('/settings');
+      break;
+
+    case 'backup':
+      ElMessageBox.confirm('确定要备份系统数据吗?', '数据备份', {
+        confirmButtonText: '确定备份',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        ElMessage.success('数据备份已开始,请勿关闭页面');
+        // 模拟备份进度
+        let progress = 0;
+        const timer = setInterval(() => {
+          progress += 20;
+          if (progress >= 100) {
+            clearInterval(timer);
+            ElMessage.success('数据备份完成!');
+          } else {
+            ElMessage.info(`备份进度:${progress}%`);
+          }
+        }, 1000);
+      }).catch(() => {
+        ElMessage.info('已取消数据备份');
+      });
+      break;
+  }
+};
 
-    &:hover {
-      transform: translateY(-5px);
-      box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
+// 数字动画效果
+const animateValue = (obj, start, end, duration) => {
+  let startTimestamp = null;
+  const step = (timestamp) => {
+    if (!startTimestamp) startTimestamp = timestamp;
+    const progress = Math.min((timestamp - startTimestamp) / duration, 1);
+    obj.value = Math.floor(progress * (end - start) + start);
+    if (progress < 1) {
+      window.requestAnimationFrame(step);
+    }
+  };
+  window.requestAnimationFrame(step);
+};
+
+onMounted(() => {
+  // 初始化时显示欢迎信息
+  /*ElMessage.success('欢迎使用IBMS智能建筑管理系统!');*/
+
+  // 模拟数据更新
+  setInterval(() => {
+    // 随机更新一些数据
+    const randomIndex = Math.floor(Math.random() * mockChartData.value.length);
+    mockChartData.value[randomIndex] = Math.floor(Math.random() * 40) + 60;
+  }, 5000);
+});
+</script>
+
+<style lang="scss" scoped>
+.dashboard-container {
+  padding: 20px;
+  background: #f5f7fa;
+  min-height: calc(100vh - 84px);
+
+  // 欢迎区域
+  .welcome-section {
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    border-radius: 12px;
+    padding: 40px;
+    margin-bottom: 30px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    color: white;
+    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
+    position: relative;
+    overflow: hidden;
+
+    &::before {
+      content: '';
+      position: absolute;
+      top: -50%;
+      right: -50%;
+      width: 200%;
+      height: 200%;
+      background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
+      animation: pulse 4s ease-in-out infinite;
+    }
+
+    .welcome-content {
+      flex: 1;
+      position: relative;
+      z-index: 1;
+
+      .welcome-title {
+        font-size: 36px;
+        margin-bottom: 10px;
+        font-weight: 600;
+
+        .gradient-text {
+          background: linear-gradient(to right, #fff, #f0f0f0);
+          -webkit-background-clip: text;
+          -webkit-text-fill-color: transparent;
+        }
+      }
+
+      .welcome-subtitle {
+        font-size: 18px;
+        opacity: 0.9;
+        margin-bottom: 20px;
+      }
+
+      .version-info {
+        .el-tag {
+          margin-right: 10px;
+        }
+      }
+    }
+
+    .welcome-illustration {
+      width: 400px;
+      height: 300px;
+      position: relative;
+      z-index: 1;
+
+      svg {
+        width: 100%;
+        height: 100%;
+        filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));
+      }
     }
   }
 
-  ul {
-    list-style-type: none;
-    padding: 0;
+  // 统计卡片
+  .stat-cards {
+    margin-bottom: 30px;
 
-    li {
-      font-size: 14px;
-      line-height: 24px;
+    .stat-card {
+      border-radius: 12px;
+      padding: 25px;
+      color: white;
       position: relative;
-      padding-left: 20px;
+      overflow: hidden;
+      transition: all 0.3s ease;
+      cursor: pointer;
+      height: 140px;
+
+      &:hover {
+        transform: translateY(-5px);
+        box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15);
+
+        .stat-icon {
+          transform: scale(1.1) rotate(5deg);
+        }
+      }
 
       &::before {
-        content: '•';
+        content: '';
+        position: absolute;
+        top: -50%;
+        right: -50%;
+        width: 200%;
+        height: 200%;
+        background: radial-gradient(circle, rgba(255,255,255,0.2) 0%, transparent 70%);
+      }
+
+      .stat-icon {
         position: absolute;
-        left: 0;
-        color: #409eff;
-        font-size: 16px;
+        right: 20px;
+        top: 20px;
+        font-size: 48px;
+        opacity: 0.3;
+        transition: all 0.3s ease;
+      }
+
+      .stat-content {
+        position: relative;
+        z-index: 1;
+
+        .stat-value {
+          font-size: 32px;
+          font-weight: 600;
+          margin-bottom: 5px;
+
+          .count-up {
+            display: inline-block;
+          }
+        }
+
+        .stat-label {
+          font-size: 16px;
+          opacity: 0.9;
+        }
+      }
+
+      .stat-trend {
+        position: absolute;
+        bottom: 20px;
+        right: 20px;
+        font-size: 14px;
+        display: flex;
+        align-items: center;
+        gap: 5px;
+        background: rgba(255,255,255,0.2);
+        padding: 4px 8px;
+        border-radius: 4px;
+
+        &.up {
+          color: #67C23A;
+        }
+
+        &.down {
+          color: #F56C6C;
+        }
+
+        .el-icon {
+          font-size: 16px;
+        }
       }
     }
   }
 
-  .update-log {
-    ol {
-      padding-left: 20px;
+  // 主要内容区域
+  .main-content {
+    // 通用卡片样式
+    .el-card {
+      border-radius: 12px;
+      border: none;
+      box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
+      margin-bottom: 20px;
+      transition: all 0.3s ease;
 
-      li {
-        font-size: 14px;
-        line-height: 24px;
+      &:hover {
+        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+      }
+
+      .card-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        h3 {
+          margin: 0;
+          font-size: 18px;
+          font-weight: 600;
+          color: #303133;
+          display: flex;
+          align-items: center;
+          gap: 8px;
+
+          .el-icon {
+            color: #409EFF;
+          }
+        }
+      }
+    }
+
+    // 功能模块卡片
+    .feature-card {
+      .module-item {
+        text-align: center;
+        padding: 20px 10px;
+        border-radius: 8px;
+        cursor: pointer;
+        transition: all 0.3s ease;
+        margin-bottom: 15px;
+
+        &:hover {
+          background: #f5f7fa;
+          transform: translateY(-3px);
+
+          .module-icon {
+            transform: scale(1.1);
+          }
+        }
+
+        .module-icon {
+          width: 60px;
+          height: 60px;
+          margin: 0 auto 12px;
+          border-radius: 12px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          transition: all 0.3s ease;
+          color: white;
+          font-size: 28px;
+        }
+
+        .module-name {
+          font-size: 16px;
+          font-weight: 600;
+          color: #303133;
+          margin-bottom: 5px;
+        }
+
+        .module-desc {
+          font-size: 13px;
+          color: #909399;
+        }
+      }
+    }
+
+    // 监控图表卡片
+    .monitor-card {
+      .chart-container {
+        height: 300px;
+        padding: 20px;
+
+        .mock-chart {
+          height: 100%;
+          display: flex;
+          flex-direction: column;
+          justify-content: flex-end;
+
+          .chart-bars {
+            flex: 1;
+            display: flex;
+            align-items: flex-end;
+            justify-content: space-around;
+            gap: 10px;
+            margin-bottom: 10px;
+
+            .bar {
+              flex: 1;
+              background: linear-gradient(to top, #409EFF, #66b1ff);
+              border-radius: 4px 4px 0 0;
+              position: relative;
+              min-height: 20px;
+              animation: growBar 1s ease-out forwards;
+              transform-origin: bottom;
+
+              &:hover {
+                opacity: 0.8;
+              }
+
+              .bar-value {
+                position: absolute;
+                top: -25px;
+                left: 50%;
+                transform: translateX(-50%);
+                font-size: 12px;
+                color: #606266;
+                white-space: nowrap;
+              }
+            }
+          }
+
+          .chart-labels {
+            display: flex;
+            justify-content: space-around;
+            color: #909399;
+            font-size: 12px;
+            padding-top: 10px;
+            border-top: 1px solid #EBEEF5;
+          }
+        }
+      }
+    }
+
+    // 技术栈卡片
+    .tech-card {
+      .tech-tabs {
+        .tech-list {
+          .tech-item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 8px 0;
+            border-bottom: 1px solid #f0f0f0;
+
+            &:last-child {
+              border-bottom: none;
+            }
+
+            .el-tag {
+              min-width: 120px;
+            }
+
+            .tech-version {
+              color: #909399;
+              font-size: 13px;
+            }
+          }
+        }
+      }
+    }
+
+    // 公告卡片
+    .notice-card {
+      :deep(.el-timeline) {
+        padding-left: 0;
+
+        .el-timeline-item__wrapper {
+          padding-left: 28px;
+        }
+
+        .el-timeline-item__timestamp {
+          color: #909399;
+          font-size: 13px;
+        }
+
+        .notice-content {
+          h4 {
+            margin: 0 0 5px 0;
+            font-size: 15px;
+            font-weight: 600;
+            color: #303133;
+          }
+
+          p {
+            margin: 0;
+            font-size: 13px;
+            color: #606266;
+            line-height: 1.5;
+          }
+        }
+      }
+    }
+
+    // 快捷操作卡片
+    .quick-action-card {
+      .quick-actions {
+        display: grid;
+        grid-template-columns: repeat(2, 1fr);
+        gap: 10px;
+
+        .el-button {
+          width: 100%;
+          height: 40px;
+          border-radius: 8px;
+          font-size: 14px;
+        }
+      }
+    }
+  }
+
+  // 响应式设计
+  @media (max-width: 768px) {
+    padding: 10px;
+
+    .welcome-section {
+      flex-direction: column;
+      text-align: center;
+      padding: 30px 20px;
+
+      .welcome-content {
+        margin-bottom: 20px;
+
+        .welcome-title {
+          font-size: 28px;
+        }
+
+        .welcome-subtitle {
+          font-size: 16px;
+        }
+      }
+
+      .welcome-illustration {
+        width: 100%;
+        max-width: 300px;
+        height: 200px;
+      }
+    }
+
+    .stat-cards {
+      .stat-card {
+        margin-bottom: 15px;
+      }
+    }
+
+    .main-content {
+      .feature-card {
+        .module-item {
+          padding: 15px 5px;
+
+          .module-icon {
+            width: 50px;
+            height: 50px;
+            font-size: 24px;
+          }
+
+          .module-name {
+            font-size: 14px;
+          }
+
+          .module-desc {
+            font-size: 12px;
+          }
+        }
+      }
+
+      .quick-action-card {
+        .quick-actions {
+          grid-template-columns: 1fr;
+        }
+      }
+    }
+  }
+}
+
+// 动画效果
+@keyframes fadeInUp {
+  from {
+    opacity: 0;
+    transform: translateY(20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+@keyframes pulse {
+  0%, 100% {
+    transform: scale(1);
+  }
+  50% {
+    transform: scale(1.1);
+  }
+}
+
+@keyframes growBar {
+  from {
+    transform: scaleY(0);
+  }
+  to {
+    transform: scaleY(1);
+  }
+}
+
+.stat-card,
+.el-card {
+  animation: fadeInUp 0.6s ease-out;
+}
+
+// 延迟动画
+.stat-cards .el-col:nth-child(1) .stat-card { animation-delay: 0.1s; }
+.stat-cards .el-col:nth-child(2) .stat-card { animation-delay: 0.2s; }
+.stat-cards .el-col:nth-child(3) .stat-card { animation-delay: 0.3s; }
+.stat-cards .el-col:nth-child(4) .stat-card { animation-delay: 0.4s; }
+.notice-card {
+  .card-header {
+    .notice-badge {
+      margin-left: 10px;
+
+      :deep(.el-badge__content) {
+        height: 18px;
+        line-height: 18px;
+        padding: 0 6px;
+        font-size: 12px;
+      }
+    }
+  }
+
+  :deep(.el-timeline) {
+    .notice-content {
+      cursor: pointer;
+      transition: all 0.3s;
+
+      &:hover {
+        background: #f5f7fa;
+        padding: 8px;
+        border-radius: 4px;
+        margin: -8px;
+      }
+
+      h4 {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+
+        .el-tag {
+          margin-left: auto;
+        }
+      }
+
+      .notice-meta {
+        margin-top: 8px;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        font-size: 12px;
+        color: #909399;
+
+        .notice-type {
+          :deep(.el-tag) {
+            height: 20px;
+            line-height: 18px;
+            padding: 0 8px;
+          }
+        }
+      }
+    }
+  }
+
+  :deep(.el-empty) {
+    padding: 20px 0;
+
+    .el-empty__description {
+      margin-top: 10px;
+    }
+  }
+}
+
+// 公告详情弹窗样式
+:global(.notice-detail-dialog) {
+  .el-message-box__content {
+    max-height: 60vh;
+    overflow-y: auto;
+  }
+}
+// 监控图表卡片
+.monitor-card {
+  .chart-container {
+    height: 350px;
+    padding: 20px;
+    cursor: pointer;
+
+    .mock-chart {
+      height: 100%;
+      display: flex;
+      flex-direction: column;
+
+      .chart-bars {
+        flex: 1;
+        display: flex;
+        align-items: flex-end;
+        justify-content: space-around;
+        gap: 10px;
+        margin-bottom: 10px;
+
+        .bar {
+          flex: 1;
+          background: linear-gradient(to top, #409EFF, #66b1ff);
+          border-radius: 4px 4px 0 0;
+          position: relative;
+          min-height: 20px;
+          animation: growBar 1s ease-out forwards;
+          transform-origin: bottom;
+          transition: all 0.3s ease;
+
+          &:hover {
+            opacity: 0.8;
+            transform: translateY(-2px);
+          }
+
+          .bar-value {
+            position: absolute;
+            top: -25px;
+            left: 50%;
+            transform: translateX(-50%);
+            font-size: 12px;
+            color: #606266;
+            white-space: nowrap;
+          }
+        }
+      }
+
+      .chart-lines {
+        flex: 1;
         position: relative;
+        margin-bottom: 10px;
+        background: #fafafa;
+        border-radius: 4px;
+        overflow: hidden;
+
+        svg {
+          display: block;
+
+          .grid {
+            opacity: 0.5;
+          }
 
-        &::before {
-          content: counter(list-item) '.';
-          position: absolute;
-          left: -20px;
-          color: #409eff;
-          font-weight: bold;
+          .data-point {
+            transition: all 0.3s ease;
+
+            &:hover {
+              r: 7;
+              fill: #66b1ff;
+            }
+          }
+
+          .value-label {
+            opacity: 0;
+            transition: opacity 0.3s ease;
+          }
+
+          &:hover .value-label {
+            opacity: 1;
+          }
         }
       }
+
+      .chart-labels {
+        display: flex;
+        justify-content: space-around;
+        color: #909399;
+        font-size: 12px;
+        padding-top: 10px;
+        border-top: 1px solid #EBEEF5;
+
+        span {
+          cursor: pointer;
+          transition: color 0.3s ease;
+
+          &:hover {
+            color: #409EFF;
+          }
+        }
+      }
+    }
+  }
+}
+
+// 添加折线动画
+@keyframes drawLine {
+  to {
+    stroke-dashoffset: 0;
+  }
+}
+
+.chart-lines {
+  polyline {
+    stroke-dasharray: 1000;
+    stroke-dashoffset: 1000;
+    animation: drawLine 2s ease-out forwards;
+  }
+
+  .data-point {
+    opacity: 0;
+    animation: fadeIn 0.5s ease-out forwards;
+
+    @for $i from 1 through 7 {
+      &:nth-child(#{$i}) {
+        animation-delay: #{$i * 0.1}s;
+      }
     }
   }
+}
 
-  .el-divider {
-    background-color: #ddd;
-    margin: 30px 0;
+@keyframes fadeIn {
+  to {
+    opacity: 1;
   }
 }
 </style>