Bläddra i källkod

feat: 故障报修

leo 1 vecka sedan
förälder
incheckning
764a794721

+ 8 - 0
pages.json

@@ -999,6 +999,14 @@
         "enablePullDownRefresh": false,
         "navigationStyle": "custom"
       }
+    },
+    {
+      "path": "pages/warning/index",
+      "style": {
+        "navigationBarTitleText": "预警列表",
+        "enablePullDownRefresh": false,
+        "navigationStyle": "custom"
+      }
     }
   ],
 

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 679 - 517
pages/afterSale/addafter.vue


+ 5 - 0
pages/deviceDetails/weatherStation/index.vue

@@ -306,6 +306,11 @@ export default {
       } else {
         url = `/pages/deviceDetails/weatherStation/${type}?deviceInfo=${encodeURIComponent(JSON.stringify(this.deviceInfo))}`
       }
+	  
+	  if(type==='devRepair'){
+		 url = `/pages/afterSale/addafter?d_id=${this.deviceInfo.d_id}&device_id=${this.deviceInfo.devBid}&device_type=${this.deviceInfo.deviceType}` 
+	  }
+	  
       uni.navigateTo({
         url
       });

+ 1 - 1
pages/equipList/index.vue

@@ -631,7 +631,7 @@ export default {
           break;
         case 46:
           uni.navigateTo({
-            url: `../deviceDetails/weatherStation/index?devBid=${item.imei}&devName=${item.device_name}&devStatus=${item.is_online}&address=${item.address}&uptime=${item.uptime}&d_id=${item.d_id}`,
+            url: `../deviceDetails/weatherStation/index?devBid=${item.imei}&devName=${item.device_name}&devStatus=${item.is_online}&address=${item.address}&uptime=${item.uptime}&d_id=${item.d_id}&deviceType=46`,
           });
           break;
         // case 45:

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 939 - 855
pages/index/index.vue


BIN
pages/warning/assets/check.png


BIN
pages/warning/assets/exclamation.png


BIN
pages/warning/assets/important.png


BIN
pages/warning/assets/normal-gray.png


BIN
pages/warning/assets/normal.png


BIN
pages/warning/assets/offline.png


BIN
pages/warning/assets/pest.png


BIN
pages/warning/assets/urgent.png


BIN
pages/warning/assets/warning.png


BIN
pages/warning/assets/weather.png


+ 845 - 0
pages/warning/index.vue

@@ -0,0 +1,845 @@
+<template>
+  <view class="warning-center">
+    <!-- 顶部导航栏 -->
+    <custom-card>
+      <block slot="backText">预警中心</block>
+    </custom-card>
+
+    <!-- 主要内容 -->
+    <view class="content">
+      <!-- 宣传语区域 -->
+      <view class="banner">
+        <view class="banner-text">
+          <text class="banner-title">天灾虫害,预警在先</text>
+          <text class="banner-subtitle">全天候监测,风险早知晓</text>
+        </view>
+        <view class="warning-icon">
+          <view class="icon-triangle">
+            <image :src="exclamation" class="icon-image" />
+          </view>
+        </view>
+      </view>
+
+      <!-- 日期选择器 -->
+      <view class="date-picker">
+        <view class="date-inputs" @click="showDatePickerHandle()">
+          <text class="date-label">{{ startTime }}</text>
+          <text class="date-separator">-</text>
+          <text class="date-label">{{ endTime }}</text>
+          <u-icon name="calendar" size="28rpx" color="#999" />
+        </view>
+        <view class="date-icons">
+          <image :src="warning" class="icon-image" />
+        </view>
+      </view>
+
+      <!-- 标签导航 -->
+      <view class="tab-nav">
+        <view 
+          v-for="(tab, index) in tabs" 
+          :key="index"
+          class="tab-item"
+          :class="{ active: activeTab === index }"
+          @click="activeTabHandler(index)"
+        >
+          {{ tab }}
+        </view>
+      </view>
+
+      <!-- 预警列表 -->
+      <scroll-view
+        v-if="activeTab == 0"
+        class="warning-list" 
+        scroll-y 
+        :scroll-top="listScrollTop" 
+        scroll-with-animation
+        @scrolltolower="loadMore"
+        @scrolltoupper="refresh"
+      >
+        <view
+          v-for="(warning, index) in warningList"
+          :key="index"
+          class="warning-item"
+          @click="popupShowHandler(warning)"
+        >
+          <view class="warning-header">
+            <view class="warning-type">
+              <view class="type-icon" :class="warning.status == '0' ? 'type-blue' : 'type-gray'">
+                <image :src="weather" class="icon-image" />
+              </view>
+              <text class="type-text">{{ warning.warning_title }}</text>
+              <text class="device-type">{{ warning.device_type }}</text>
+            </view>
+            <view class="warning-level" :class="warning.level == '0' ? 'level-normal' :warning.level == '1'? 'level-important':'level-urgent'">
+            </view>
+          </view>
+          <view class="warning-content">
+            {{ warning.warning_content }}
+          </view>
+          <view class="warning-footer">
+            <text class="warning-time">{{ formatTime(warning.upl_time) }}</text>
+            <view class="warning-location">
+              <u-icon name="location" size="20rpx" color="#999" />
+            </view>
+          </view>
+          <view class="location-text">
+            <u-icon name="map" size="28"></u-icon>
+            <text style="margin-left: 10rpx">{{ warning.address || '-' }}</text>
+          </view>
+        </view>
+        
+        <!-- 加载更多提示 -->
+        <view class="loading-more">
+          <text v-if="loading">加载中...</text>
+          <text v-else-if="finished">没有更多数据了</text>
+          <text v-else>上拉加载更多</text>
+        </view>
+      </scroll-view>
+
+      <scroll-view
+        v-if="activeTab == 1"
+        class="warning-list" 
+        scroll-y 
+        :scroll-top="listScrollTop" 
+        scroll-with-animation
+        @scrolltolower="loadMore"
+        @scrolltoupper="refresh"
+      >
+        <view
+          v-for="(warning, index) in manualWarningList"
+          :key="index"
+          class="warning-item"
+          @click="popupManualShowHandler(warning)"
+        >
+          <view class="warning-content">
+            {{ warning.conf }}
+          </view>
+          <view class="warning-footer">
+            <text class="warning-time">{{ formatTime(warning.create_time) }}</text>
+            <view class="warning-location">
+              <u-icon name="location" size="20rpx" color="#999" />
+            </view>
+          </view>
+        </view>
+        
+        <!-- 加载更多提示 -->
+        <view class="loading-more">
+          <text v-if="loading">加载中...</text>
+          <text v-else-if="finished">没有更多数据了</text>
+          <text v-else>上拉加载更多</text>
+        </view>
+      </scroll-view>
+      <!-- 回到顶部 -->
+      <view class="back-to-top" @click="scrollToTop">
+        <u-icon name="arrow-up" size="28rpx" color="#999" />
+      </view>
+    </view>
+		<u-calendar v-model="showDatePicker" :mode="mode" @change="changeCalendar"></u-calendar>
+    <u-popup v-model="popupShow" mode="bottom" height="80%" border-radius="20">
+      <view class="popup-container">
+        <view class="popup-icon" :class="current.level == '0' ? 'popup-level-normal' :current.level == '1'? 'popup-level-important':'popup-level-urgent'">
+        </view>
+        <view class="popup-header">{{ current.warning_title }}</view>
+        <view class="popup-type">
+          <text class="type-text">{{ current.device_type }}</text>
+        </view>
+        <view class="popup-content">{{ current.warning_content }}</view>
+        <view class="popup-time">{{ formatTime(current.upl_time) }}</view>
+        <view class="popup-text">
+          <u-icon name="map" size="28"></u-icon>
+          <text style="margin-left: 10rpx">{{ warning.address || '-' }}</text>
+        </view>
+      </view>
+		</u-popup>
+    <u-popup v-model="popupManualShow" mode="bottom" height="80%" border-radius="20">
+      <view class="popup-container" style="margin-top:40rpx">
+        <view class="popup-content">{{ currentManual.conf }}</view>
+        <view class="popup-time">{{ formatTime(currentManual.create_time) }}</view>
+      </view>
+		</u-popup>
+  </view>
+</template>
+
+<script>
+import exclamation from './assets/exclamation.png';
+import warning from './assets/warning.png';
+import pest from './assets/pest.png';
+import weather from './assets/weather.png';
+import offline from './assets/offline.png';
+
+export default {
+  data() {
+    return {
+      current:{},
+      currentManual:{},
+      popupShow: false,
+      popupManualShow: false,
+      exclamation,
+      warning,
+      pest,
+      weather,
+      offline,
+      showDatePicker: false,
+			mode: 'range',
+      activeTab: 0,
+      listScrollTop: 0,
+      tabs: ['环境监测','手动预警'],
+      warningList: [],
+      manualWarningList: [],
+      page_size: 10,
+      page: 1,
+      // 获取7天前的日期
+      startTime: this.getSevenDaysAgo() || '开始日期',
+      endTime: this.getCurrentDate() || '结束日期',
+      deviceId: '',
+      device_type_id:'',
+      total: 0,
+      loading: false,
+      finished: false,
+      refreshing: false,
+    };
+  },
+  onLoad() {
+    this.getWarningList();
+  },
+  methods: {
+    activeTabHandler(index){
+      this.activeTab = index;
+      if(index == 0){
+        this.getWarningList();
+      }else if(index == 1){
+        this.getWarningManualList();
+      }
+    },
+    popupManualShowHandler(warning){
+      this.currentManual = warning;
+      this.popupManualShow = true;
+    },
+    popupShowHandler(warning){
+      this.current = warning;
+      const params = {
+        record_ids: warning.id,
+      };
+      this.readHandler(params)
+      this.popupShow = true;
+    },
+    changeCalendar(e){
+      this.startTime = e.startDate;
+      this.endTime = e.endDate;
+      // 重置分页参数
+      this.page = 1;
+      this.loading = false;
+      this.finished = false;
+      if(this.activeTab == 0){
+        this.getWarningList();
+      }else if(this.activeTab == 1){
+        this.getWarningManualList();
+      }
+    },
+    // 加载更多
+    loadMore() {
+      if (this.finished) return;
+      if(this.activeTab == 0){
+        this.getWarningList();
+      }else if(this.activeTab == 1){
+        this.getWarningManualList();
+      }
+    },
+    // 下拉刷新
+    refresh() {
+      if (this.loading) return;
+      // 重置分页参数
+      this.page = 1;
+      this.loading = false;
+      this.finished = false;
+      this.refreshing = true;
+      if(this.activeTab == 0){
+        this.getWarningList();
+      }else if(this.activeTab == 1){
+        this.getWarningManualList();
+      }
+    },
+    // 已读
+    async readHandler(params){
+      await this.$myRequest({
+        url:'/api/api_gateway?method=device.env_sec_alert.update_env_sec_alert_record',
+        method:'POST',
+        data: params,
+      })
+      this.page = 1;
+      if(this.activeTab == 0){
+        this.getWarningList();
+      }
+    },
+    formatTime(time){
+      if(!time){
+        return '';
+      }
+      const date = new Date(time * 1000);
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      const hour = String(date.getHours()).padStart(2, '0');
+      const minute = String(date.getMinutes()).padStart(2, '0');
+      const second = String(date.getSeconds()).padStart(2, '0');
+      return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
+    },
+    // 获取7天前的日期
+    getSevenDaysAgo() {
+      const today = new Date();
+      const sevenDaysAgo = new Date(today);
+      sevenDaysAgo.setDate(today.getDate() - 7);
+      const year = sevenDaysAgo.getFullYear();
+      const month = String(sevenDaysAgo.getMonth() + 1).padStart(2, '0');
+      const day = String(sevenDaysAgo.getDate()).padStart(2, '0');
+      return `${year}-${month}-${day}`;
+    },
+    // 获取当前日期
+    getCurrentDate() {
+      const today = new Date();
+      const year = today.getFullYear();
+      const month = String(today.getMonth() + 1).padStart(2, '0');
+      const day = String(today.getDate()).padStart(2, '0');
+      return `${year}-${month}-${day}`;
+    },
+    showDatePickerHandle(){
+      this.showDatePicker = true;
+    },
+  // 把日期转成秒数
+    dateToTimestamp(date) {
+      return Math.floor(new Date(date).getTime() / 1000);
+    },
+    async getWarningManualList(){
+      if (this.loading) return;
+      
+      this.loading = true;
+      
+      const start = this.dateToTimestamp(this.startTime + ' 00:00:00');
+      const end = this.dateToTimestamp(this.endTime + ' 23:59:59');
+      const params = {
+        start,
+        end,
+        page: this.page,
+        page_size: this.page_size,
+        device_id: this.deviceId,
+        device_type_id: this.device_type_id
+      }
+      
+      try {
+        const res = await this.$myRequest({
+          url:'/api/api_gateway?method=device.env_sec_alert.get_manual_env_sec_alert_record',
+          method:'POST',
+          data: params,
+        })
+        
+        const resData = res?.data || [];
+        const list = resData || [];
+        
+        if (this.page === 1) {
+          this.manualWarningList = list;
+        } else {
+          this.manualWarningList = [...this.manualWarningList, ...list];
+        }
+        
+        this.total = res?.total || 0;
+        this.loading = false;
+        
+        // 计算是否还有更多数据
+        if (this.manualWarningList.length >= this.total) {
+          this.finished = true;
+        } else {
+          this.page++;
+        }
+        
+        // 结束刷新状态
+        if (this.refreshing) {
+          this.refreshing = false;
+        }
+        
+      } catch (error) {
+        console.error('获取预警列表失败:', error);
+        this.loading = false;
+        if (this.refreshing) {
+          this.refreshing = false;
+        }
+      }
+    },
+    async getWarningList(){
+      if (this.loading) return;
+      
+      this.loading = true;
+      
+      const start = this.dateToTimestamp(this.startTime + ' 00:00:00');
+      const end = this.dateToTimestamp(this.endTime + ' 23:59:59');
+      const params = {
+        start,
+        end,
+        page: this.page,
+        page_size: this.page_size,
+        device_id: this.deviceId,
+        device_type_id: this.device_type_id
+      }
+      
+      try {
+        const res = await this.$myRequest({
+          url:'/api/api_gateway?method=device.env_sec_alert.get_env_sec_alert',
+          method:'POST',
+          data: params,
+        })
+        
+        const resData = res?.data || [];
+        const list = resData || [];
+        
+        if (this.page === 1) {
+          this.warningList = list;
+        } else {
+          this.warningList = [...this.warningList, ...list];
+        }
+        
+        this.total = res?.total_num || 0;
+        this.loading = false;
+        
+        // 计算是否还有更多数据
+        if (this.warningList.length >= this.total) {
+          this.finished = true;
+        } else {
+          this.page++;
+        }
+        
+        // 结束刷新状态
+        if (this.refreshing) {
+          this.refreshing = false;
+        }
+        
+      } catch (error) {
+        console.error('获取预警列表失败:', error);
+        this.loading = false;
+        if (this.refreshing) {
+          this.refreshing = false;
+        }
+      }
+    },
+    // 回到顶部
+    scrollToTop() {
+      this.listScrollTop = 0;
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.warning-center {
+  min-height: 100vh;
+  font-family: 'Source Han Sans CN';
+  background: linear-gradient(0deg, #F5F6FA 79.64%, #FFEEEC 99.35%);
+}
+::v-deep .u-calendar__action{
+  display:flex;
+}
+::v-deep .u-calendar__action__icon{
+  width: 40rpx;
+}
+::v-deep .u-calendar__action__text{
+  width: calc(100% - 160rpx);
+  text-align: center;
+}
+.content {
+  padding: 0 32rpx;
+}
+
+/* 宣传语区域 */
+.banner {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin: 32rpx 0;
+  margin-bottom: 0rpx;
+  padding: 24rpx;
+
+  .banner-text {
+    flex: 1;
+
+    .banner-title {
+      font-style: italic;
+      display: block;
+      color: #303133;
+      font-family: "Source Han Sans CN VF";
+      font-size: 40rpx;
+      font-weight: 700;
+      margin-bottom: 8rpx;
+    }
+
+    .banner-subtitle {
+      display: block;
+      font-size: 24rpx;
+      color: #666666;
+    }
+  }
+
+  .warning-icon {
+    .icon-triangle {
+      width: 130rpx;
+      height: 130rpx;
+      position: relative;
+      .icon-image{
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+}
+.popup-container{
+  position:relative;
+  .popup-icon {
+    position: absolute;
+    top:0;
+    right:0;
+    margin-left: 12rpx;
+    width: 112rpx;
+    height: 48rpx;
+    background: url('./assets/normal-gray.png');
+    background-repeat: no-repeat;
+    background-size: 100%;
+    &.popup-level-normal {
+      background: url('./assets/normal.png');
+      background-repeat: no-repeat;
+      background-size: 100%;
+    }
+
+    &.popup-level-important {
+      background: url('./assets/important.png');
+      background-repeat: no-repeat;
+      background-size: 100%;
+    }
+
+    &.popup-level-urgent {
+      background: url('./assets/urgent.png');
+      background-repeat: no-repeat;
+      background-size: 100%;
+    }
+  }
+}
+.popup-header{
+  padding: 20rpx 32rpx;
+  width: 100%;
+  text-align: left;
+  color: #303133;
+  font-family: "Source Han Sans CN VF";
+  font-size: 32rx;
+  font-weight: 700;
+}
+.popup-type{
+  border-radius:32rpx;
+  margin-left: 32rpx;
+  display:inline-block;
+  border: 2rpx solid #E4E7ED;
+  padding: 2rpx 12rpx;
+  color: #666666;
+  font-family: "Source Han Sans CN VF";
+  font-size: 24rpx;
+  font-weight: 400;
+  margin-bottom: 24rpx;
+}
+.popup-content{
+  padding: 0 32rpx;
+  line-height: 50rpx;
+}
+.popup-time{
+  padding:0 32rpx;
+  margin-top: 36rpx;
+  color:#999999;
+  font-size: 24rpx;
+  text-align: left;
+  color: #bdbdbd;
+  font-family: "Source Han Sans CN VF";
+  font-style: normal;
+  font-weight: 400;
+}
+.popup-location{
+  font-size: 24rpx;
+  margin-top: 16rpx;
+  padding-top: 16rpx;
+  font-family: "Source Han Sans CN VF";
+  color: #999999;
+  display:flex;
+  align-items: center;
+}
+
+.popup-text {
+  padding: 0 32rpx;
+  font-size: 24rpx;
+  margin-top: 16rpx;
+  padding-top: 16rpx;
+  font-family: "Source Han Sans CN VF";
+  color: #999999;
+  display:flex;
+  align-items: center;
+}
+/* 日期选择器 */
+.date-picker {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 32rpx;
+
+  .date-inputs {
+    display: flex;
+    align-items: center;
+    background: #ffffff;
+    border-radius: 48rpx;
+    padding: 14rpx;
+    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+
+    .date-label {
+      width:250rpx;
+      font-size: 26rpx;
+      color: #999999;
+      text-align: center;
+    }
+
+    .date-separator {
+      margin: 0 24rpx;
+      font-size: 26rpx;
+      color: #999999;
+    }
+  }
+
+  .date-icons {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 60rpx            ;
+    height: 60rpx;
+    border-radius: 50%;
+    background:#ffffff;
+    .icon-image{
+      width: 32rpx;
+      height: 32rpx;
+    }
+  }
+}
+
+/* 标签导航 */
+.tab-nav {
+  display: flex;
+  margin-bottom: 24rpx;
+  border-bottom: 1rpx solid #e5e5e5;
+
+  .tab-item {
+    padding: 20rpx 0;
+    font-size: 28rpx;
+    color: #666666;
+    position: relative;
+    margin-right: 30rpx;
+    &.active {
+      color: #333333;
+      font-weight: 500;
+
+      &::after {
+        content: '';
+        position: absolute;
+        bottom: 0;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 100%;
+        height: 4rpx;
+        background: #515153;
+        border-radius: 2rpx;
+      }
+    }
+  }
+}
+
+/* 预警列表 */
+.warning-list {
+  margin-bottom: 100rpx;
+  height: calc(100vh - 600rpx);
+  overflow-y: auto;
+  .warning-item {
+    position: relative;
+    padding: 28rpx 24rpx;
+    margin-bottom: 24rpx;
+    background: #ffffff;
+    border-radius: 16rpx;
+    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
+    .warning-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 16rpx;
+
+      .warning-type {
+        display: flex;
+        align-items: center;
+        flex: 1;
+        min-width: 0;
+
+        .type-icon {
+          width: 48rpx;
+          height: 48rpx;
+          border-radius: 50%;
+          margin-right: 12rpx;
+          flex-shrink: 0;
+          display:flex;
+          align-items: center;
+          justify-content: center;
+
+          &.type-red {
+            background: linear-gradient(180deg, #FFA9A5 0%, #FF716A 100%);
+          }
+
+          &.type-blue {
+            background: linear-gradient(180deg, #83D2FF 0%, #09A5FC 100%);
+          }
+
+          &.type-yellow {
+            background: linear-gradient(180deg, #FED057 0%, #FFAF40 100%);
+          }
+
+          &.type-gray {
+            background: #DDDFE6;
+          }
+          .icon-image{
+            width: 28rpx;
+            height: 28rpx;
+          }
+        }
+
+        .type-text {
+          font-size: 26rpx;
+          font-weight: 500;
+          color: #333333;
+          margin-right: 12rpx;
+          flex-shrink: 0;
+        }
+
+        .device-type {
+          font-size: 22rpx;
+          color: #515153;
+          font-family: "Source Han Sans CN VF";
+          font-style: normal;
+          font-weight: 400;
+          border: 2rpx solid #E4E7ED;
+          padding: 2rpx 12rpx;
+          border-radius: 32rpx;
+          margin-left: 8rpx;
+          flex-shrink: 0;
+        }
+      }
+
+      .warning-level {
+        position: absolute;
+        top:0;
+        right:0;
+        margin-left: 12rpx;
+        width: 112rpx;
+        height: 48rpx;
+        background: url('./assets/normal-gray.png');
+        background-repeat: no-repeat;
+        background-size: 100%;
+        &.level-normal {
+          background: url('./assets/normal.png');
+          background-repeat: no-repeat;
+          background-size: 100%;
+        }
+
+        &.level-important {
+          background: url('./assets/important.png');
+          background-repeat: no-repeat;
+          background-size: 100%;
+        }
+
+        &.level-urgent {
+          background: url('./assets/urgent.png');
+          background-repeat: no-repeat;
+          background-size: 100%;
+        }
+      }
+    }
+
+    .warning-content {
+      font-size: 28rpx;
+      color: #333333;
+      line-height: 40rpx;
+      margin-bottom: 20rpx;
+      //最多显示2行
+      overflow: hidden;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical;
+    }
+
+    .warning-footer {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+
+      .warning-time {
+        font-size: 24rpx;
+        color: #999999;
+      }
+
+      .warning-location {
+        display: flex;
+        align-items: center;
+      }
+    }
+    .location-text {
+      font-size: 24rpx;
+      margin-top: 16rpx;
+      padding-top: 16rpx;
+      border-top: 1rpx solid #f0f0f0;
+      font-family: "Source Han Sans CN VF";
+      color: #999999;
+      display:flex;
+      align-items: center;
+    }
+  }
+}
+
+/* 加载更多提示 */
+.loading-more {
+  text-align: center;
+  padding: 32rpx 0;
+  font-size: 24rpx;
+  color: #999999;
+}
+
+/* 回到顶部 */
+.back-to-top {
+  position: fixed;
+  bottom: 80rpx;
+  right: 32rpx;
+  width: 64rpx;
+  height: 64rpx;
+  background: #ffffff;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
+  z-index: 99;
+}
+
+/* 顶部导航栏右侧图标 */
+.header-right {
+  display: flex;
+  align-items: center;
+
+  .notification-icon {
+    position: relative;
+    margin-right: 32rpx;
+
+    .badge {
+      position: absolute;
+      top: -4rpx;
+      right: -4rpx;
+      width: 12rpx;
+      height: 12rpx;
+      background: #ff6b6b;
+      border-radius: 50%;
+    }
+  }
+}
+</style>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 4 - 0
static/images/cb/control.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 559 - 0
static/images/cb/dev-station.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 4 - 0
static/images/cb/setting.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 8 - 0
static/images/cb/sim.svg


static/images/device/冻土.svg → static/images/device/dongtu.svg