From 074bcb8394fab66ce531c219e1e7de7c142ff2d5 Mon Sep 17 00:00:00 2001
From: doum <doum>
Date: 星期五, 29 五月 2026 11:07:10 +0800
Subject: [PATCH] 新增智能电表、空调管理

---
 admin/src/views/index.vue | 1002 +++++++++++++++++++++++++++++++++++-----------------------
 1 files changed, 600 insertions(+), 402 deletions(-)

diff --git a/admin/src/views/index.vue b/admin/src/views/index.vue
index 51f20e9..6df214f 100644
--- a/admin/src/views/index.vue
+++ b/admin/src/views/index.vue
@@ -1,109 +1,139 @@
 <template>
-  <div class="main">
-    <div class="main_left">
-      <div class="main_left_head">
-        <div class="head_item">
-          <div class="head_item_left">
-            <span>寰呭姙宸ュ崟</span>
-            <span>{{obj.waitDealWorkOrderSize || 0}}</span>
-            <span @click="jump(1)">鏌ョ湅鏇村</span>
-          </div>
-          <img class="head_item_icon" src="@/assets/indexIcon/ic_daibangongdan@2x.png" />
-        </div>
-        <div class="head_item">
-          <div class="head_item_left">
-            <span>寰呭贰妫�</span>
-            <span>{{obj.waitTaskSize || 0}}</span>
-            <span @click="jump(2)">鏌ョ湅鏇村</span>
-          </div>
-          <img class="head_item_icon" src="@/assets/indexIcon/ic_daixuncha@2x.png" />
-        </div>
-        <div class="head_item">
-          <div class="head_item_left">
-            <span>寰呯洏鐐�</span>
-            <span>{{obj.stocktakingSize || 0}}</span>
-            <span @click="jump(3)">鏌ョ湅鏇村</span>
-          </div>
-          <img class="head_item_icon" src="@/assets/indexIcon/ic_daipandian@2x.png" />
-        </div>
-      </div>
-      <div class="main_left_menu">
-        <div class="main_left_menu_title">
-          <span>甯哥敤鍔熻兘</span>
-          <div class="main_left_menu_title_edit" @click="$refs.commonFunctions.open('甯哥敤鍔熻兘绠$悊')">
-            <img src="@/assets/indexIcon/ic_daibangongdan@2x.png" />
-            <span>鑷畾涔夊姛鑳�</span>
-          </div>
-        </div>
-        <div class="main_left_menu_list">
-          <div class="list_item" v-for="(item, index) in list" :key="index" @click="jump1(item.path)">
-            <div class="list_item_left">
-              <img :src="item.icoPath" />
-              <span>{{item.name}}</span>
+  <div class="dashboard">
+    <section class="dashboard-top">
+      <div class="dashboard-top__left">
+        <div class="stat-cards card">
+          <div class="stat-cards__item head_item--workorder" @click="jump(1)">
+            <div class="stat-cards__content">
+              <span class="stat-cards__label">寰呭姙宸ュ崟</span>
+              <span class="stat-cards__value">{{ obj.waitDealWorkOrderSize || 0 }}</span>
+              <span class="stat-cards__link">鏌ョ湅鏇村</span>
             </div>
-            <i class="el-icon-arrow-right"></i>
+            <img class="stat-cards__icon" src="@/assets/indexIcon/ic_daibangongdan@2x.png" alt="" />
           </div>
-          <div class="list_item1"></div>
-          <div class="list_item1"></div>
-          <div class="list_item1"></div>
+          <div class="stat-cards__item head_item--inspect" @click="jump(2)">
+            <div class="stat-cards__content">
+              <span class="stat-cards__label">寰呭贰妫�</span>
+              <span class="stat-cards__value">{{ obj.waitTaskSize || 0 }}</span>
+              <span class="stat-cards__link">鏌ョ湅鏇村</span>
+            </div>
+            <img class="stat-cards__icon" src="@/assets/indexIcon/ic_daixuncha@2x.png" alt="" />
+          </div>
+          <div class="stat-cards__item head_item--stock" @click="jump(3)">
+            <div class="stat-cards__content">
+              <span class="stat-cards__label">寰呯洏鐐�</span>
+              <span class="stat-cards__value">{{ obj.stocktakingSize || 0 }}</span>
+              <span class="stat-cards__link">鏌ョ湅鏇村</span>
+            </div>
+            <img class="stat-cards__icon" src="@/assets/indexIcon/ic_daipandian@2x.png" alt="" />
+          </div>
         </div>
-      </div>
-    </div>
-    <div class="main_right">
-      <div class="main_right_title">鏃ョ▼</div>
-      <div class="main_right_search">
-        <el-select v-model="nian" style="width: 150px; margin-right: 15px;" @change="getMonthNoticess" placeholder="璇烽�夋嫨">
-          <el-option
-            v-for="item in yearList"
-            :key="item.val"
-            :label="item.name"
-            :value="item.val">
-          </el-option>
-        </el-select>
-        <el-select v-model="yue" style="width: 150px;" @change="getMonthNoticess" placeholder="璇烽�夋嫨">
-          <el-option
-            v-for="(item, index) in 12"
-            :key="index"
-            :label="item + '鏈�'"
-            :value="item > 9 ? item : `0${item}`">
-          </el-option>
-        </el-select>
-      </div>
-      <div class="main_right_date">
-        <Calendar
-          ref="Calendar"
-          v-on:choseDay="clickDay"
-          :markDateMore="markDateMore"
-        ></Calendar>
-      </div>
-      <div class="main_right_rc">
-        <div class="main_right_rc_title">褰撴棩鏃ョ▼锛坽{dataList.length}}锛�</div>
-        <div class="main_right_rc_list" v-loading="loading">
-          <div class="main_right_rc_list_row" v-for="(item, index) in dataList" :key="index">
-            <div class="top">
-              <div class="top_left">
-                <div class="top_left_dian"></div>
-                <div class="top_left_title">{{item.title}}</div>
+
+        <div class="quick-menu card">
+          <div class="section-header">
+            <h3 class="section-header__title">甯哥敤鍔熻兘</h3>
+            <div class="section-header__action" @click="$refs.commonFunctions.open('甯哥敤鍔熻兘绠$悊')">
+              <i class="el-icon-setting"></i>
+              <span>鑷畾涔夊姛鑳�</span>
+            </div>
+          </div>
+          <div class="quick-menu__grid">
+            <div
+              class="quick-menu__item"
+              v-for="(item, index) in list"
+              :key="index"
+              @click="jump1(item.path)"
+            >
+              <div class="quick-menu__item-left">
+                <img :src="item.icoPath" alt="" />
+                <span>{{ item.name }}</span>
               </div>
-              <div class="top_date">{{item.param1}}</div>
-            </div>
-            <div class="bottom">
-              {{item.content}}
+              <i class="el-icon-arrow-right quick-menu__arrow"></i>
             </div>
           </div>
         </div>
       </div>
-    </div>
+
+      <aside class="schedule-panel card">
+        <div class="section-header">
+          <h3 class="section-header__title">鏃ョ▼</h3>
+        </div>
+        <div class="schedule-panel__filters">
+          <el-select v-model="nian" class="schedule-panel__select" @change="getMonthNoticess" placeholder="璇烽�夋嫨">
+            <el-option
+              v-for="item in yearList"
+              :key="item.val"
+              :label="item.name"
+              :value="item.val"
+            />
+          </el-select>
+          <el-select v-model="yue" class="schedule-panel__select" @change="getMonthNoticess" placeholder="璇烽�夋嫨">
+            <el-option
+              v-for="(item, index) in 12"
+              :key="index"
+              :label="item + '鏈�'"
+              :value="item > 9 ? item : `0${item}`"
+            />
+          </el-select>
+        </div>
+        <div class="schedule-panel__calendar">
+          <Calendar
+            ref="Calendar"
+            v-on:choseDay="clickDay"
+            :markDateMore="markDateMore"
+          />
+        </div>
+        <div class="schedule-panel__list-wrap">
+          <div class="schedule-panel__list-title">褰撴棩鏃ョ▼锛坽{ dataList.length }}锛�</div>
+          <div class="schedule-panel__list" v-loading="loading">
+            <template v-if="dataList.length">
+              <div class="schedule-item" v-for="(item, index) in dataList" :key="index">
+                <div class="schedule-item__top">
+                  <div class="schedule-item__title-wrap">
+                    <div class="schedule-item__dot"></div>
+                    <div class="schedule-item__title">{{ item.title }}</div>
+                  </div>
+                  <div class="schedule-item__time">{{ item.param1 }}</div>
+                </div>
+                <div class="schedule-item__content">{{ item.content }}</div>
+              </div>
+            </template>
+            <div v-else class="schedule-empty">褰撴棩鏆傛棤鏃ョ▼</div>
+          </div>
+        </div>
+      </aside>
+    </section>
+
+    <section class="dashboard-warning">
+      <ElectricalWarningWorkbench />
+    </section>
+
+    <section class="dashboard-energy">
+      <DailyEnergyTrendPanel
+        title="杩�30澶╂櫤鑳界數琛ㄦ瘡鏃ユ�荤數閲�/鎬荤數璐�"
+        :load-data="electricalDailyEnergyStats"
+      />
+      <DailyEnergyTrendPanel
+        title="杩�30澶╃┖璋冨鑱旀満姣忔棩鎬荤數閲�/鎬荤數璐�"
+        :load-data="conditionerDailyEnergyStats"
+      />
+    </section>
+
     <CommonFunctions ref="commonFunctions" @success="getYwQuickLists" />
   </div>
 </template>
 
 <script>
 import CommonFunctions from '@/components/business/commonFunctions'
-import { getYwQuickList, getMonthNotices, workDeskData } from '@/api/ywWorkDesk'
+import ElectricalWarningWorkbench from '@/components/business/ElectricalWarningWorkbench'
+import DailyEnergyTrendPanel from '@/components/business/DailyEnergyTrendPanel'
+import { getYwQuickList, getMonthNotices, workDeskData, electricalDailyEnergyStats, conditionerDailyEnergyStats } from '@/api/ywWorkDesk'
+import { navigateByMenu } from '@/utils/menu'
+import { mapState } from 'vuex'
 import Calendar from 'vue-calendar-component'
+
 export default {
-  data() {
+  data () {
     return {
       list: [],
       value: new Date(),
@@ -116,16 +146,21 @@
       loading: false
     }
   },
-  components: { CommonFunctions, Calendar },
+  components: { CommonFunctions, Calendar, ElectricalWarningWorkbench, DailyEnergyTrendPanel },
+  computed: {
+    ...mapState(['menuData'])
+  },
   created () {
     this.getWorkDeskData()
     this.getYwQuickLists()
     this.getYear()
   },
   methods: {
-    getYear() {
-      let currentYear = new Date().getFullYear();
-      let currentMonth = new Date().getMonth() + 1;
+    electricalDailyEnergyStats,
+    conditionerDailyEnergyStats,
+    getYear () {
+      const currentYear = new Date().getFullYear()
+      const currentMonth = new Date().getMonth() + 1
       this.nian = currentYear
       this.yue = currentMonth > 9 ? currentMonth : `0${currentMonth}`
       for (let i = currentYear - 10; i <= currentYear; i++) {
@@ -133,40 +168,27 @@
       }
       this.getMonthNoticess()
     },
-    // 鑾峰彇浠e姙
-    getWorkDeskData() {
-      workDeskData({})
-        .then(res => {
-          this.obj = res
-        })
-    },
-    clickDay(e) {
-      this.loading = true
-      let date = e.replace("/\\//g", "-")
-      let arr = this.markDateMore.filter(item => {
-        if (item.date === date) {
-          return item
-        }
+    getWorkDeskData () {
+      workDeskData({}).then(res => {
+        this.obj = res
       })
-      if (arr.length > 0) {
-        this.dataList = arr[0].noticeList
-      } else {
-        this.dataList = []
-      }
+    },
+    clickDay (e) {
+      this.loading = true
+      const date = e.replace('/\\//g', '-')
+      const arr = this.markDateMore.filter(item => item.date === date)
+      this.dataList = arr.length > 0 ? arr[0].noticeList : []
       this.loading = false
     },
-    // 鑾峰彇鏃ョ▼
-    getMonthNoticess() {
+    getMonthNoticess () {
       getMonthNotices(this.nian + '-' + this.yue).then(res => {
-        let arr = res.filter(item => {
-          if (item.noticeList && item.noticeList.length > 0) {
-            return item
-          }
-        })
-        this.markDateMore = arr.map(item => {
-          return { date: item.monthDate.replace("/-0/g", "-"), className: 'markRed', noticeList: item.noticeList }
-        })
-        let toDay = this.getDay()
+        const arr = res.filter(item => item.noticeList && item.noticeList.length > 0)
+        this.markDateMore = arr.map(item => ({
+          date: item.monthDate.replace('/-0/g', '-'),
+          className: 'markRed',
+          noticeList: item.noticeList
+        }))
+        const toDay = this.getDay()
         this.markDateMore.forEach(item => {
           if (item.date === toDay) {
             this.dataList = item.noticeList
@@ -175,301 +197,477 @@
         this.$refs.Calendar.ChoseMonth(`${this.nian}-${this.yue}`, false)
       })
     },
-    getYwQuickLists() {
-      getYwQuickList({})
-        .then(res => {
-          this.list = res
-        })
+    getYwQuickLists () {
+      getYwQuickList({}).then(res => {
+        this.list = res
+      })
     },
-    getDay() {
-      let date = new Date();
-      let year = date.getFullYear();  // 鑾峰彇骞翠唤
-      let month = String(date.getMonth() + 1).padStart(2, '0');  // 鑾峰彇鏈堜唤锛屽苟琛ラ浂
-      let day = String(date.getDate()).padStart(2, '0');  // 鑾峰彇鏃ユ湡锛屽苟琛ラ浂
-  
-      return `${year}-${month}-${day}`;
+    getDay () {
+      const date = new Date()
+      const year = date.getFullYear()
+      const month = String(date.getMonth() + 1).padStart(2, '0')
+      const day = String(date.getDate()).padStart(2, '0')
+      return `${year}-${month}-${day}`
     },
-    jump(type) {
-      if (type === 1) {
-        this.$router.push({ path: '/workorder/workorderList' })
-      } else if (type === 2) {
-        this.$router.push({ path: '/Inspection/task' })
-      } else if (type === 3) {
-        this.$router.push({ path: '/stock/check' })
+    jump (type) {
+      const pathMap = {
+        1: '/workorder/workorderList',
+        2: '/Inspection/task',
+        3: '/stock/check'
       }
+      this.navigateToMenu(pathMap[type])
     },
-    jump1(path) {
-      this.$router.push({ path })
+    jump1 (path) {
+      this.navigateToMenu(path)
+    },
+    navigateToMenu (path) {
+      navigateByMenu(this.$router, this.$store, path, this.menuData.list)
     }
   }
 }
 </script>
+
 <style lang="scss" scoped>
-  .main {
-    width: 100%;
-    padding: 15px;
-    box-sizing: border-box;
-    background: #F4F7FC;
+.dashboard {
+  --dash-primary: #3E80EF;
+  --dash-primary-light: rgba(62, 128, 239, 0.08);
+  --dash-text: #222222;
+  --dash-text-secondary: #666666;
+  --dash-text-muted: #999999;
+  --dash-bg: #F4F7FC;
+  --dash-card-bg: #FFFFFF;
+  --dash-border: #EEF2F8;
+  --dash-radius: 10px;
+  --dash-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
+  --dash-gap: 16px;
+
+  width: 100%;
+  min-height: 100%;
+  padding: var(--dash-gap);
+  box-sizing: border-box;
+  background: var(--dash-bg);
+  display: flex;
+  flex-direction: column;
+  gap: var(--dash-gap);
+}
+
+.card {
+  background: var(--dash-card-bg);
+  border-radius: var(--dash-radius);
+  border: 1px solid var(--dash-border);
+  box-shadow: var(--dash-shadow);
+}
+
+.section-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 16px;
+
+  &__title {
+    margin: 0;
+    padding-left: 10px;
+    font-weight: 600;
+    font-size: 18px;
+    color: var(--dash-text);
+    line-height: 1.4;
+    border-left: 3px solid var(--dash-primary);
+  }
+
+  &__action {
+    cursor: pointer;
     display: flex;
-    align-items: self-start;
+    align-items: center;
+    gap: 4px;
+    font-size: 14px;
+    color: var(--dash-primary);
+    transition: opacity 0.2s;
+
+    &:hover {
+      opacity: 0.8;
+    }
+
+    i {
+      font-size: 15px;
+    }
+  }
+}
+
+.dashboard-top {
+  display: flex;
+  align-items: flex-start;
+  gap: var(--dash-gap);
+
+  &__left {
+    flex: 1;
+    min-width: 0;
+    display: flex;
+    flex-direction: column;
+    gap: var(--dash-gap);
+  }
+}
+
+.stat-cards {
+  display: flex;
+  align-items: stretch;
+  gap: 12px;
+  padding: 20px;
+
+  &__item {
+    flex: 1;
+    display: flex;
+    align-items: center;
     justify-content: space-between;
-    .main_left {
-      flex: 1;
-      height: 100%;
-      display: flex;
-      flex-direction: column;
-      .main_left_head {
-        width: 100%;
-        height: 199px;
-        background: #FFFFFF;
-        border-radius: 8px;
-        display: flex;
-        align-items: center;
-        padding: 20px;
-        box-sizing: border-box;
-        .head_item {
-          flex: 1;
-          height: 100%;
-          display: flex;
-          align-items: center;
-          justify-content: space-between;
-          background: #F1F5FF;
-          border-radius: 8px;
-          margin-right: 15px;
-          padding: 28px 20px;
-          box-sizing: border-box;
-          &:last-child {
-            margin: 0 !important;
-          }
-          .head_item_left {
-            flex: 1;
-            display: flex;
-            flex-direction: column;
-            span {
-              &:nth-child(1) {
-                font-weight: 600;
-                font-size: 17px;
-                color: #222222;
-              }
-              &:nth-child(2) {
-                font-weight: 600;
-                font-size: 30px;
-                color: #111111;
-                margin-top: 6px;
-                margin-bottom: 14px;
-              }
-              &:nth-child(3) {
-                font-weight: 400;
-                font-size: 13px;
-                color: #999999;
-                cursor: pointer;
-              }
-            }
-          }
-          .head_item_icon {
-            width: 60px;
-            height: 60px;
-          }
-        }
-      }
-      .main_left_menu {
-        width: 100%;
-        height: calc(100% - 214px);
-        background: #FFFFFF;
-        border-radius: 8px;
-        margin-top: 15px;
-        padding: 20px;
-        box-sizing: border-box;
-        .main_left_menu_title {
-          width: 100%;
-          display: flex;
-          align-items: center;
-          justify-content: space-between;
-          span {
-            font-weight: 600;
-            font-size: 18px;
-            color: #222222;
-          }
-          .main_left_menu_title_edit {
-            cursor: pointer;
-            display: flex;
-            align-items: center;
-            img {
-              width: 15px;
-              height: 15px;
-            }
-            span {
-              font-weight: 400;
-              font-size: 14px;
-              color: #3E80EF;
-              margin-left: 5px;
-            }
-          }
-        }
-        .main_left_menu_list {
-          width: 100%;
-          margin-top: 20px;
-          display: flex;
-          align-items: center;
-          flex-wrap: wrap;
-          justify-content: space-between;
-          .list_item1 {
-            width: 24%;
-            height: 0;
-          }
-          .list_item {
-            cursor: pointer;
-            width: 24%;
-            height: 70px;
-            background: #FFFFFF;
-            border-radius: 8px;
-            border: 1px solid #EEEEEE;
-            padding: 0 16px;
-            box-sizing: border-box;
-            margin-bottom: 15px;
-            display: flex;
-            align-items: center;
-            justify-content: space-between;
-            .list_item_left {
-              display: flex;
-              align-items: center;
-              img {
-                width: 40px;
-                height: 40px;
-                margin-right: 5px;
-              }
-              span {
-                font-weight: 500;
-                font-size: 15px;
-                color: #222222;
-              }
-            }
-          }
-        }
+    padding: 24px 20px;
+    border-radius: 8px;
+    cursor: pointer;
+    transition: transform 0.2s, box-shadow 0.2s;
+
+    &:hover {
+      transform: translateY(-2px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+
+      .stat-cards__icon {
+        transform: scale(1.05);
       }
     }
-    .main_right {
-      flex-shrink: 0;
-      width: 500px;
-      height: 100%;
-      margin-left: 15px;
-      background: #FEFEFF;
-      border-radius: 8px;
-      padding: 20px;
-      box-sizing: border-box;
-      .main_right_title {
-        font-weight: 600;
-        font-size: 18px;
-        color: #222222;
-        margin-bottom: 12px;
+
+    &.head_item--workorder {
+      background: linear-gradient(135deg, #EEF4FF 0%, #F5F8FF 100%);
+
+      .stat-cards__value {
+        color: #3E80EF;
       }
-      .main_right_rc {
-        width: 100%;
-        display: flex;
-        flex-direction: column;
-        .main_right_rc_title {
-          font-weight: 500;
-          font-size: 16px;
-          color: #222222;
-          margin-bottom: 15px;
-        }
-        .main_right_rc_list {
-          width: 100%;
-          height: 300px;
-          overflow-y: scroll;
-          .main_right_rc_list_row {
-            width: 100%;
-            padding: 15px;
-            box-sizing: border-box;
-            background: #F4F7FC;
-            border-radius: 2px;
-            margin-bottom: 10px;
-            .top {
-              width: 100%;
-              display: flex;
-              align-items: center;
-              justify-content: space-between;
-              .top_left {
-                display: flex;
-                align-items: center;
-                .top_left_dian {
-                  width: 10px;
-                  height: 10px;
-                  border-radius: 50%;
-                  background: #FF9E00;
-                  margin-right: 10px;
-                }
-                .top_left_title {
-                  font-weight: 500;
-                  font-size: 15px;
-                  color: #222222;
-                }
-              }
-              .top_date {
-                font-weight: 400;
-                font-size: 12px;
-                color: #999999;
-              }
-            }
-            .bottom {
-              font-weight: 400;
-              font-size: 13px;
-              color: #666666;
-              margin-top: 8px;
-            }
-          }
-        }
+
+      .stat-cards__link:hover {
+        color: #3E80EF;
       }
-      .main_right_date {
-        width: 100%;
-        padding-top: 15px;
-        box-sizing: border-box;
-        ::v-deep .wh_content_item {
-          height: 50px;
-          color: #222222;
-          font-weight: 400;
-          font-size: 15px;
-        }
-        ::v-deep .wh_item_date {
-          width: 30px;
-          height: 30px;
-          font-size: 15px;
-        }
-        ::v-deep .wh_content_all {
-          background-color: #ffffff;
-        }
-        ::v-deep .wh_top_changge {
-          display: none;
-        }
-        ::v-deep .wh_container {
-          max-width: 100%;
-        }
-        ::v-deep .wh_content_item .wh_isToday {
-          background-color: rgba(62, 128, 239, 0.47);
-          color: #ffffff;
-        }
-        ::v-deep .wh_item_date:hover {
-          background-color: #3E80EF;
-          border-radius: 50%;
-          color: #ffffff;
-        }
-        ::v-deep .wh_content_item .wh_chose_day {
-          background: #3E80EF;
-          color: #fff;
-        }
-        ::v-deep .markRed {
-          position: relative;
-        }
-        ::v-deep .markRed::after {
-          content: '鈼�';
-          color: #FF9E00;
-          font-size: 11px;
-          position: absolute;
-          bottom: -30px;
-          left: 50%;
-          transform: translate(-50%, 0);
-        }
+    }
+
+    &.head_item--inspect {
+      background: linear-gradient(135deg, #E8FAF0 0%, #F2FBF6 100%);
+
+      .stat-cards__value {
+        color: #36B37E;
+      }
+
+      .stat-cards__link:hover {
+        color: #36B37E;
+      }
+    }
+
+    &.head_item--stock {
+      background: linear-gradient(135deg, #FFF6E8 0%, #FFFAF2 100%);
+
+      .stat-cards__value {
+        color: #FF9E00;
+      }
+
+      .stat-cards__link:hover {
+        color: #FF9E00;
       }
     }
   }
+
+  &__content {
+    display: flex;
+    flex-direction: column;
+  }
+
+  &__label {
+    font-weight: 600;
+    font-size: 16px;
+    color: var(--dash-text);
+  }
+
+  &__value {
+    font-weight: 700;
+    font-size: 32px;
+    line-height: 1.2;
+    margin: 6px 0 12px;
+  }
+
+  &__link {
+    font-size: 13px;
+    color: var(--dash-text-muted);
+    transition: color 0.2s;
+  }
+
+  &__icon {
+    width: 56px;
+    height: 56px;
+    flex-shrink: 0;
+    transition: transform 0.2s;
+  }
+}
+
+.quick-menu {
+  padding: 20px;
+
+  &__grid {
+    display: grid;
+    grid-template-columns: repeat(4, 1fr);
+    gap: 12px;
+  }
+
+  &__item {
+    cursor: pointer;
+    height: 72px;
+    background: var(--dash-card-bg);
+    border-radius: 8px;
+    border: 1px solid #EEEEEE;
+    padding: 0 16px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s;
+
+    &:hover {
+      border-color: var(--dash-primary);
+      box-shadow: 0 2px 8px rgba(62, 128, 239, 0.12);
+      transform: translateY(-1px);
+
+      .quick-menu__arrow {
+        color: var(--dash-primary);
+      }
+    }
+  }
+
+  &__item-left {
+    display: flex;
+    align-items: center;
+    min-width: 0;
+
+    img {
+      width: 40px;
+      height: 40px;
+      margin-right: 8px;
+      flex-shrink: 0;
+    }
+
+    span {
+      font-weight: 500;
+      font-size: 14px;
+      color: var(--dash-text);
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+  }
+
+  &__arrow {
+    color: #C0C4CC;
+    font-size: 14px;
+    flex-shrink: 0;
+    transition: color 0.2s;
+  }
+}
+
+.schedule-panel {
+  flex-shrink: 0;
+  width: 480px;
+  padding: 20px;
+
+  &__filters {
+    display: flex;
+    gap: 12px;
+    margin-bottom: 12px;
+  }
+
+  &__select {
+    width: 140px;
+  }
+
+  &__calendar {
+    padding: 12px;
+    background: #FAFBFE;
+    border-radius: 8px;
+    border: 1px solid var(--dash-border);
+  }
+
+  &__list-wrap {
+    margin-top: 16px;
+  }
+
+  &__list-title {
+    font-weight: 500;
+    font-size: 15px;
+    color: var(--dash-text);
+    margin-bottom: 12px;
+  }
+
+  &__list {
+    height: 280px;
+    overflow-y: auto;
+    padding-right: 4px;
+
+    &::-webkit-scrollbar {
+      width: 5px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+      background: #D8DCE6;
+      border-radius: 3px;
+    }
+  }
+}
+
+.schedule-item {
+  padding: 14px 14px 14px 12px;
+  background: #FAFBFE;
+  border-radius: 8px;
+  border: 1px solid var(--dash-border);
+  border-left: 3px solid #FF9E00;
+  margin-bottom: 10px;
+
+  &__top {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 8px;
+  }
+
+  &__title-wrap {
+    display: flex;
+    align-items: center;
+    min-width: 0;
+  }
+
+  &__dot {
+    width: 8px;
+    height: 8px;
+    border-radius: 50%;
+    background: #FF9E00;
+    margin-right: 8px;
+    flex-shrink: 0;
+  }
+
+  &__title {
+    font-weight: 500;
+    font-size: 14px;
+    color: var(--dash-text);
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  &__time {
+    font-size: 12px;
+    color: var(--dash-text-muted);
+    flex-shrink: 0;
+  }
+
+  &__content {
+    font-size: 13px;
+    color: var(--dash-text-secondary);
+    margin-top: 8px;
+    line-height: 1.5;
+  }
+}
+
+.schedule-empty {
+  height: 120px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: var(--dash-text-muted);
+  font-size: 14px;
+}
+
+.dashboard-warning,
+.dashboard-energy {
+  display: flex;
+  flex-direction: column;
+  gap: var(--dash-gap);
+}
+
+::v-deep .electrical-warning-panel,
+::v-deep .daily-energy-panel {
+  margin-top: 0 !important;
+  border: 1px solid var(--dash-border);
+  box-shadow: var(--dash-shadow);
+  border-radius: var(--dash-radius);
+}
+
+.schedule-panel__calendar {
+  ::v-deep .wh_content_item {
+    height: 50px;
+    color: var(--dash-text);
+    font-size: 14px;
+  }
+
+  ::v-deep .wh_item_date {
+    width: 30px;
+    height: 30px;
+    font-size: 14px;
+  }
+
+  ::v-deep .wh_content_all {
+    background-color: transparent;
+  }
+
+  ::v-deep .wh_top_changge {
+    display: none;
+  }
+
+  ::v-deep .wh_container {
+    max-width: 100%;
+  }
+
+  ::v-deep .wh_content_item .wh_isToday {
+    background-color: rgba(62, 128, 239, 0.47);
+    color: #ffffff;
+  }
+
+  ::v-deep .wh_item_date:hover {
+    background-color: var(--dash-primary);
+    border-radius: 50%;
+    color: #ffffff;
+  }
+
+  ::v-deep .wh_content_item .wh_chose_day {
+    background: var(--dash-primary);
+    color: #fff;
+  }
+
+  ::v-deep .markRed {
+    position: relative;
+  }
+
+  ::v-deep .markRed::after {
+    content: '鈼�';
+    color: #FF9E00;
+    font-size: 11px;
+    position: absolute;
+    bottom: -30px;
+    left: 50%;
+    transform: translate(-50%, 0);
+  }
+}
+
+@media (max-width: 1400px) {
+  .dashboard-top {
+    flex-direction: column;
+  }
+
+  .schedule-panel {
+    width: 100%;
+  }
+}
+
+@media (max-width: 1200px) {
+  .quick-menu__grid {
+    grid-template-columns: repeat(3, 1fr);
+  }
+
+  .stat-cards {
+    flex-direction: column;
+  }
+}
+
+@media (max-width: 768px) {
+  .quick-menu__grid {
+    grid-template-columns: repeat(2, 1fr);
+  }
+}
 </style>

--
Gitblit v1.9.3