浏览代码

smartCity页面框架

Alfyn 4 周之前
父节点
当前提交
188a18f1b2

+ 1 - 0
package.json

@@ -36,6 +36,7 @@
     "mockjs2": "1.0.8",
     "moment": "^2.29.2",
     "nprogress": "^0.2.0",
+    "postcss-pxtorem": "^6.1.0",
     "pptx-preview": "^1.0.1",
     "store": "^2.0.12",
     "viser-vue": "^2.4.8",

+ 6 - 1
postcss.config.js

@@ -1,5 +1,10 @@
 module.exports = {
   plugins: {
-    autoprefixer: {}
+    autoprefixer: {},
+      'postcss-pxtorem': {
+        rootValue: 19, // 表示根元素字体大小或根据input参数返回根元素字体大小
+        propList: ['*'], // 可以从px更改为rem的属性, 通配符*表示启用所有属性
+        selectorBlackList: ['.norem'] // 过滤掉.norem开头的class,不进行rem转换
+    }
   }
 }

+ 5 - 0
public/index.html

@@ -8,6 +8,11 @@
   <link rel="icon" href="<%= BASE_URL %>LPC_Logo.png">
   <title>AutoCDE</title>
   <style>
+    @font-face {
+        font-family: 'DS-Digital';
+        src:url(./static/font/DS-DIGI-1.ttf);
+    }
+
     .first-loading-wrp {
       display: flex;
       justify-content: center;

二进制
public/static/font/DS-DIGI-1.ttf


+ 47 - 1
public/static/iconfont/demo_index.html

@@ -64,6 +64,18 @@
                 <div class="code-name">&amp;#xe62f;</div>
               </li>
           
+            <li class="dib">
+              <span class="icon iconfont">&#xe696;</span>
+                <div class="name">甲醛</div>
+                <div class="code-name">&amp;#xe696;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7e6;</span>
+                <div class="name">二氧化碳</div>
+                <div class="code-name">&amp;#xe7e6;</div>
+              </li>
+          
             <li class="dib">
               <span class="icon iconfont">&#xe66e;</span>
                 <div class="name">新增公司账号</div>
@@ -94,7 +106,7 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.ttf?t=1734072766493') format('truetype');
+  src: url('iconfont.ttf?t=1739346800770') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -138,6 +150,24 @@
             </div>
           </li>
           
+          <li class="dib">
+            <span class="icon iconfont icon-jiaquan"></span>
+            <div class="name">
+              甲醛
+            </div>
+            <div class="code-name">.icon-jiaquan
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-eryanghuatan"></span>
+            <div class="name">
+              二氧化碳
+            </div>
+            <div class="code-name">.icon-eryanghuatan
+            </div>
+          </li>
+          
           <li class="dib">
             <span class="icon iconfont icon-xinzenggongsizhanghao"></span>
             <div class="name">
@@ -199,6 +229,22 @@
                 <div class="code-name">#icon-pengzhuang</div>
             </li>
           
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-jiaquan"></use>
+                </svg>
+                <div class="name">甲醛</div>
+                <div class="code-name">#icon-jiaquan</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-eryanghuatan"></use>
+                </svg>
+                <div class="name">二氧化碳</div>
+                <div class="code-name">#icon-eryanghuatan</div>
+            </li>
+          
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#icon-xinzenggongsizhanghao"></use>

+ 9 - 1
public/static/iconfont/iconfont.css

@@ -1,6 +1,6 @@
 @font-face {
   font-family: "iconfont"; /* Project id  */
-  src: url('iconfont.ttf?t=1734072766493') format('truetype');
+  src: url('iconfont.ttf?t=1739346800770') format('truetype');
 }
 
 .iconfont {
@@ -19,6 +19,14 @@
   content: "\e62f";
 }
 
+.icon-jiaquan:before {
+  content: "\e696";
+}
+
+.icon-eryanghuatan:before {
+  content: "\e7e6";
+}
+
 .icon-xinzenggongsizhanghao:before {
   content: "\e66e";
 }

文件差异内容过多而无法显示
+ 0 - 0
public/static/iconfont/iconfont.js


+ 14 - 0
public/static/iconfont/iconfont.json

@@ -19,6 +19,20 @@
       "unicode": "e62f",
       "unicode_decimal": 58927
     },
+    {
+      "icon_id": "4880072",
+      "name": "甲醛",
+      "font_class": "jiaquan",
+      "unicode": "e696",
+      "unicode_decimal": 59030
+    },
+    {
+      "icon_id": "19990401",
+      "name": "二氧化碳",
+      "font_class": "eryanghuatan",
+      "unicode": "e7e6",
+      "unicode_decimal": 59366
+    },
     {
       "icon_id": "21956665",
       "name": "新增公司账号",

二进制
public/static/iconfont/iconfont.ttf


+ 9 - 0
src/api/openPlatform/index.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+export function getOpenPlatformAPI(params) {
+  return request({
+    url: `/open-platform`,
+    method: 'get',
+    params
+  })
+}

二进制
src/assets/img/smartCity/boder.gif


二进制
src/assets/img/smartCity/empty.png


二进制
src/assets/img/smartCity/pm10.png


二进制
src/assets/img/smartCity/pm2.5.png


二进制
src/assets/img/smartCity/rightBg.png


二进制
src/assets/img/smartCity/title.png


+ 22 - 1
src/config/router.config.js

@@ -772,6 +772,17 @@ export const asyncRouterMap = [
           permission: ['IOT'],
         },
       },
+      {
+        path: '/SmartCity',
+        name: 'SmartCity',
+        component: RouteView,
+        meta: {
+          title: 'menu.SmartCity',
+          keepAlive: true,
+          icon: iot,
+          permission: ['Smart City'],
+        },
+      },
       {
         path: '/mail',
         name: 'mail',
@@ -795,7 +806,7 @@ export const asyncRouterMap = [
     ],
   },
   {
-    path: '/modelViewIOT',
+    path: '/modelView/IOT',
     name: 'ModelViewIOT',
     component: () => import('@/views/IOT/index.vue'),
     meta: { 
@@ -804,6 +815,16 @@ export const asyncRouterMap = [
       icon: dashboard,
     },
   },
+  {
+    path: '/modelView/smartCity',
+    name: 'ModelViewSmartCity',
+    component: () => import('@/views/smartCity/index.vue'),
+    meta: { 
+      title: 'menu.SmartCity',
+      keepAlive: false,
+      icon: dashboard,
+    },
+  },
   {
     path: '/modelView',
     name: 'ModelView',

+ 1 - 0
src/locales/lang/en-US/menu.js

@@ -73,4 +73,5 @@ export default {
     'menu.model-combine': 'Model Combine',
     'menu.model-initiateCollision': 'InitiateCollision',
     'menu.IOT': 'IOT',
+    'menu.SmartCity': 'SmartCity',
 }

+ 1 - 0
src/locales/lang/zh-CN/menu.js

@@ -73,4 +73,5 @@ export default {
     'menu.model-combine': '合模',
     'menu.model-initiateCollision': '碰撞检测',
     'menu.IOT': 'IOT',
+    'menu.SmartCity': 'SmartCity',
 }

+ 1 - 0
src/locales/lang/zh-HK/menu.js

@@ -73,4 +73,5 @@ export default {
     'menu.model-combine': '合模',
     'menu.model-initiateCollision': '碰撞檢測',
     'menu.IOT': 'IOT',
+    'menu.SmartCity': 'SmartCity',
 }

+ 3 - 0
src/main.js

@@ -31,6 +31,9 @@ import uploader from 'vue-simple-uploader'
 import VueXss from 'vue-xss'
 import func from '@/utils/preload'
 
+//rem屏幕适配
+import './utils/rem'
+
 Vue.prototype.$func = func;
 Vue.use(VueXss)
 Vue.use(uploader)

+ 11 - 1
src/permission.js

@@ -109,7 +109,17 @@ router.beforeEach(async (to, from, next) => {
             data: { bim_view },
           } = await fileBimView('1207')
           localStorage.setItem(bim_view.lightweightName, JSON.stringify([bim_view]))
-          next({ path: `/modelViewIOT?id=${bim_view.lightweightName}` })
+          next({ path: `/modelView/IOT?id=${bim_view.lightweightName}` })
+        }
+        if (to.path === '/SmartCity') {
+          let {
+            data: { bim_view },
+          } = await fileBimView('1392')
+          let {
+            data: { bim_view:bim_view1 },
+          } = await fileBimView('1393')
+          localStorage.setItem(bim_view.lightweightName, JSON.stringify([bim_view,bim_view1]))
+          next({ path: `/modelView/smartCity?id=${bim_view.lightweightName}` })
         }
         next()
       }

+ 21 - 0
src/utils/rem.js

@@ -0,0 +1,21 @@
+import { throttle } from './util'
+// 基准大小
+
+// 设置 rem 函数
+function setRem() {
+  const baseSize = 18
+  // 当前页面宽度相对于 1920 宽的缩放比例,可根据自己需要修改。
+  const scale = document.documentElement.clientWidth / 1920
+
+  // 设置页面根节点字体大小, 字体大小最小为12
+  let fontSize =
+    baseSize * Math.min(scale, 2) > 12 ? baseSize * Math.min(scale, 2) : 12
+  document.documentElement.style.fontSize = fontSize + 'px'
+}
+//初始化
+setRem()
+//改变窗口大小时重新设置 rem,这里最好加上节流
+window.onresize = function () {
+  // throttle(setRem, 1000)
+  throttle(setRem(), 1000)
+}

+ 14 - 0
src/utils/util.js

@@ -284,6 +284,20 @@ export async function initPathParams(objectType,objectId) {
   return  routeParams
 }
 
+// 节流函数     参数 函数 ,多久之后才能在执行一次
+export function throttle(func, wait) {
+  let previous = 0
+  return function (that) {
+    let now = Date.now()
+    let context = that
+    let args = arguments
+    if (now - previous > wait) {
+      func.apply(context, args)
+      previous = now
+    }
+  }
+}
+
 function initRouteParamsFolder(folder,parent_id) {
   const obj = {
     id: folder.id,

+ 238 - 0
src/views/smartCity/CarbonAndEnergy.vue

@@ -0,0 +1,238 @@
+<template>
+  <div class="content" :key='rendKey'>
+    <div class="title">
+      <a-icon type="double-right" /> Carbon Data <a-icon class="close-btn" type="close" @click="closeModal" />
+    </div>
+    <div class="data-list">
+      <div class="data-list-item">
+        <div><img src="@/assets/temperature.png" class="icon" />Emission</div>
+        <div>
+          <span>848.131 </span> tCO<sub>2</sub>e
+          <a-icon type="line-chart" class="chart-icon" @click="handldClickIcon('Emission')" />
+        </div>
+      </div>
+      <div class="data-list-item">
+        <div><img src="@/assets/humidity.png" class="icon" />Reduction & Offset</div>
+        <div>
+          <span>407.102 </span> tCO<sub>2</sub>e
+          <a-icon type="line-chart" class="chart-icon" @click="handldClickIcon('Reduction&Offset')" />
+        </div>
+      </div>
+      <div class="data-list-item">
+        <div><img src="@/assets/img/smartCity/pm2.5.png" class="icon" />Target</div>
+        <div><span>32</span> % <a-icon type="line-chart" class="chart-icon" @click="handldClickIcon('Target')" /></div>
+      </div>
+    </div>
+    <template v-if="type === 'Emission'">
+      <div class="title"><a-icon type="double-right" /> Carbon Emission by Category</div>
+      <div class="chart-view" ref="PieChart1"></div>
+      <div class="chart-view" ref="PieChart2"></div>
+    </template>
+    <template v-else-if="type === 'Reduction&Offset'">
+      <div class="title"><a-icon type="double-right" /> Path of Carbon Neutrality</div>
+      <div class="chart-view" ref="BarChart"></div>
+    </template>
+    <div class="empty" v-if="!type">
+      <div><img src="@/assets/img/smartCity/empty.png" alt="" /></div>
+      <div>Click the icon to load the chart data</div>
+    </div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+import echartshelp from './echartshelp'
+export default {
+  props: ['showModule'],
+  data() {
+    return {
+      type: null,
+      PieData1: {
+        xName: ['Category 1', 'Category 2', 'Category 3', 'Category 4', 'Category 5', 'Category 6'],
+        xData: ['16641', '14000', '122990', '674500', '20000', '0'],
+      },
+      PieData2: {
+        xName: [
+          'Materials (A1 - A3)',
+          'Transport to Site (A4)',
+          'Construction Works (A5)',
+          'Energy Use (B6)',
+          'Water Use (B7)',
+          'Waste (B8)',
+          'Associated Operation (B9)',
+        ],
+        xData: ['80742', '9005', '68992', '23571', '6', '32978', '413993'],
+      },
+      barData: {
+        xName: [2021, 2022, 2023, 2024, 2025],
+        xData: [
+          [678504.8, 848131, 932944.1, 1119532.92, 1231486.212],
+          [-67850.48, -254439.3, -373177.64, -671719.752, -985188.9696],
+        ],
+      },
+      barEchartView: null,
+      pie1EchartView: null,
+      pie2EchartView: null,
+      rendKey:0,
+    }
+  },
+  watch: {},
+  async mounted() {
+  },
+  methods: {
+    handldClickIcon(val) {
+      this.rendKey++
+      this.type = val
+      if (this.barEchartView) this.barEchartView.clear()
+      if (this.pie1EchartView) this.pie1EchartView.clear()
+      if (this.pie2EchartView) this.pie2EchartView.clear()
+      if (this.type === 'Emission') {
+        this.loadPie1Chart('Emission by ISO 14064', 'PieChart1', this.PieData1, this.pie1EchartView)
+        this.loadPie1Chart('Emission by PA S 2080', 'PieChart2', this.PieData2, this.pie2EchartView)
+      }
+      if (this.type === 'Reduction&Offset') {
+        this.loadBarChart()
+      }
+    },
+    loadPie1Chart(title, ref, data, chartView) {
+      this.$nextTick(() => {
+        const copyOption = JSON.parse(JSON.stringify(echartshelp.PieChartOption))
+        chartView = echarts.init(this.$refs[ref], 'light')
+        var colorarr = ['#1AC9FF', '#1ACACC', '#FFD728', '#FF9E2C', '#E86A74']
+        copyOption.color = colorarr
+        var datainfo = []
+        for (var i = 0; i < data.xData.length; i++) {
+          datainfo.push({
+            name: data.xName[i],
+            value: data.xData[i],
+          })
+        }
+
+        copyOption.title = {
+          text: title,
+          bottom: '-5px',
+          left: '50%',
+          textAlign: 'center',
+          textStyle: {
+            color: '#cfd3dc',
+            fontSize: 14,
+          },
+        }
+        copyOption.legend.left = '45%'
+        copyOption.legend.data = data.xName
+        copyOption.legend.formatter = function (name) {
+          var legenditem = []
+          for (var i = 0; i < datainfo.length; i++) {
+            if (name === datainfo[i].name) {
+              legenditem = datainfo[i]
+              break
+            }
+          }
+          return '{a|' + legenditem.name + '}'
+        }
+
+        copyOption.series[0].data = datainfo
+        copyOption.series[0].label.normal.formatter = [`{a|${datainfo[0].value}}`, `{b|${datainfo[0].name}}`].join('\n')
+
+        chartView.setOption(copyOption)
+        chartView.on('mouseover', (params) => {
+          let op = chartView.getOption()
+          op.series[0].label.formatter = [`{a|${params.data.value}}`, `{b|${params.name}}`].join('\n')
+          if (params.seriesIndex != 1) {
+            chartView.setOption(op, true)
+          }
+        })
+      })
+    },
+    loadBarChart() {
+      this.$nextTick(() => {
+        const copyOption = JSON.parse(JSON.stringify(echartshelp.barChartOption))
+        copyOption.grid.containLabel = 'true'
+        copyOption.xAxis.data = this.barData.xName
+        copyOption.legend.bottom = '-5px'
+        copyOption.series[0].data = this.barData.xData[0]
+        copyOption.series[0].name = 'Carbon Emission ( tCO2e)'
+        copyOption.series[1].data = this.barData.xData[1]
+        copyOption.series[1].name = 'Reduction & Offset ( tCO2e)'
+        this.barEchartView = echarts.init(this.$refs.BarChart)
+        this.barEchartView.setOption(copyOption)
+      })
+    },
+    closeModal() {
+      this.$emit('update:showModule', null)
+    },
+  },
+}
+</script>
+
+<style scoped lang="less">
+.content {
+  display: flex;
+  flex-direction: column;
+  color: #fff;
+  height: 100%;
+  width: 100%;
+  .title {
+    position: relative;
+    margin: 10px;
+    font-size: 18px;
+    .close-btn {
+      position: absolute;
+      right: 10px;
+      top: 5px;
+    }
+  }
+  .data-list {
+    margin: 18px 36px;
+    background-image: url(~@/assets/img/smartCity/rightBg.png);
+    background-size: 100% 100%;
+    .icon {
+      width: 18px;
+      height: 18px;
+      margin-right: 5px;
+    }
+    .chart-icon {
+      margin-left: 5px;
+      color: #1890ff;
+      cursor: pointer;
+    }
+    .data-list-item {
+      padding: 10px 15px;
+      font-size: 18px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      div {
+        display: flex;
+        align-items: center;
+        font-size: 15px;
+        color: #c0c4cc;
+      }
+      span {
+        color: #fff;
+        font-size: 18px;
+        margin-right: 5px;
+      }
+    }
+  }
+
+  .empty {
+    height: 25vh;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+
+    img {
+      height: 100px;
+      width: 100px;
+    }
+  }
+
+  .chart-view {
+    height: 22vh;
+    width: 100%;
+    margin-bottom: 20px;
+  }
+}
+</style>

+ 242 - 0
src/views/smartCity/ESG.vue

@@ -0,0 +1,242 @@
+<template>
+  <div class="content">
+    <div class="Pie-chart">
+      <div class="title"><a-icon type="double-right" /> ESG Data by Type</div>
+      <a-tooltip>
+        <template slot="title">
+          PreView Detail 
+        </template>
+        <a-icon class="link-btn" type="export" @click="preView" />
+      </a-tooltip>
+      <a-icon class="close-btn" type="close" @click="closeModal" />
+      <a-icon type="right" v-if="barTab < 2" class="switch-icon" style="top: 50%; right: 2%" @click="barTab++" />
+      <a-icon type="left" v-if="barTab > 0" class="switch-icon" style="top: 50%; left: 2%" @click="barTab--" />
+      <div style="height: 100%; width: 100%" ref="PieChart"></div>
+      <div class="subscript">{{ subscriptArr[barTab] }}</div>
+    </div>
+    <div class="line-chart">
+      <div class="title"><a-icon type="double-right" /> ESG Data in Past</div>
+      <a-icon type="right" v-if="lineTab < 3" class="switch-icon" style="top: 50%; right: 2%" @click="lineTab++" />
+      <a-icon type="left" v-if="lineTab > 0" class="switch-icon" style="top: 50%; left: 2%" @click="lineTab--" />
+      <div style="height: 100%; width: 100%" ref="lineChart"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+import echartshelp from './echartshelp'
+import { getFileById } from '@/api/document/index.js'
+export default {
+  props: ['showModule'],
+  data() {
+    return {
+      barTab: 0,
+      lineTab: 0,
+      PieData: [
+        {
+          xName: ['Exceeder Targer', 'On Track', 'Off Track'],
+          xData: [2, 7, 2],
+        },
+        {
+          xName: ['Exceeder Targer', 'On Track', 'Off Track'],
+          xData: [9, 7, 1],
+        },
+        {
+          xName: ['Exceeder Targer', 'On Track', 'Off Track'],
+          xData: [2, 10, 0],
+        },
+      ],
+      lineData: [
+        50, 52, 55, 58, 60, 62, 63, 65, 68, 70, 72, 75, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 105, 108,
+        110, 112, 114, 115, 117, 120, 123, 126, 129,
+      ],
+      lineXData: [
+        '2021/1/1',
+        '2021/2/1',
+        '2021/3/1',
+        '2021/4/1',
+        '2021/5/1',
+        '2021/6/1',
+        '2021/7/1',
+        '2021/8/1',
+        '2021/9/1',
+        '2021/10/1',
+        '2021/11/1',
+        '2021/12/1',
+        '2022/1/1',
+        '2022/2/1',
+        '2022/3/1',
+        '2022/4/1',
+        '2022/5/1',
+        '2022/6/1',
+        '2022/7/1',
+        '2022/8/1',
+        '2022/9/1',
+        '2022/10/1',
+        '2022/11/1',
+        '2022/12/1',
+        '2023/1/1',
+        '2023/2/1',
+        '2023/3/1',
+        '2023/4/1',
+        '2023/5/1',
+        '2023/6/1',
+        '2023/7/1',
+        '2023/8/1',
+        '2023/9/1',
+        '2023/10/1',
+        '2023/11/1',
+        '2023/12/1',
+      ],
+      subscriptArr: ['Environmental', 'Social', 'Governance'],
+    }
+  },
+  watch: {
+    barTab(val) {
+      this.initPieChart(val)
+    },
+    lineTab(val) {
+      this.initLineChart(val)
+    },
+  },
+  async mounted() {
+    this.initPieChart(0)
+    this.initLineChart(0)
+  },
+  methods: {
+    initPieChart(barTab) {
+      const copyOption = JSON.parse(JSON.stringify(echartshelp.PieChartOption))
+      this.deviceEchartView = echarts.init(this.$refs.PieChart, 'light')
+      var colorarr = ['#1AC9FF', '#1ACACC', '#FFD728', '#FF9E2C', '#E86A74']
+      copyOption.color = colorarr
+      var datainfo = []
+      for (var i = 0; i < this.PieData[barTab].xData.length; i++) {
+        datainfo.push({
+          name: this.PieData[barTab].xName[i],
+          value: this.PieData[barTab].xData[i],
+        })
+      }
+      copyOption.legend.left = '60%'
+      copyOption.legend.data = this.PieData[barTab].xName
+      copyOption.legend.formatter = function (name) {
+        var legenditem = []
+        for (var i = 0; i < datainfo.length; i++) {
+          if (name === datainfo[i].name) {
+            legenditem = datainfo[i]
+            break
+          }
+        }
+        return '{a|' + legenditem.name + '}'
+      }
+      copyOption.series[0].data = datainfo
+
+      copyOption.series[0].label.normal.formatter = [`{a|${datainfo[0].value}}`, `{b|${datainfo[0].name}}`].join('\n')
+      this.deviceEchartView.setOption(copyOption)
+      this.deviceEchartView.on('mouseover', (params) => {
+        let op = this.deviceEchartView.getOption()
+        op.series[0].label.formatter = [`{a|${params.data.value}}`, `{b|${params.name}}`].join('\n')
+        if (params.seriesIndex != 1) {
+          this.deviceEchartView.setOption(op, true)
+        }
+      })
+    },
+    initLineChart(lineTab) {
+      var myChart = echarts.init(this.$refs.lineChart)
+      let copyOption = JSON.parse(JSON.stringify(echartshelp.lineChartOption))
+      copyOption.xAxis.data = this.lineXData.slice(lineTab * 6, lineTab * 6 + 6)
+      copyOption.series[0].data = this.lineData.slice(lineTab * 6, lineTab * 6 + 6)
+      myChart.setOption(copyOption)
+    },
+    async preView() {
+      const { data } = await getFileById('1394')
+      const { href } = this.$router.resolve({
+        path: '/Preview',
+        query: {
+          id: data.id,
+          extension: data.extension,
+          size: data.size,
+          download_url: data.download_url
+        },
+      })
+      window.open(href, '_blank')
+    },
+    closeModal() {
+      this.$emit('update:showModule', null)
+    },
+  },
+}
+</script>
+
+<style scoped lang="less">
+.content {
+  height: 100%;
+  width: 100%;
+  color: #fff;
+  .Pie-chart {
+    position: relative;
+    height: 45%;
+    width: 100%;
+    top: 5%;
+    .title {
+      position: absolute;
+      top: -5%;
+      left: 10px;
+      font-size: 20px;
+      width: 100%;
+    }
+    .link-btn {
+      position: absolute;
+      top: -2%;
+      right: 35px;
+      font-size: 18px;
+      cursor: pointer;
+      z-index: 9;
+    }
+    .close-btn {
+      position: absolute;
+      top: -2%;
+      right: 10px;
+      font-size: 18px;
+      cursor: pointer;
+      z-index: 9;
+    }
+    .subscript {
+      position: absolute;
+      color: #cfd3dc;
+      bottom: 5%;
+      left: 50%;
+      transform: translate(-50%, 50%);
+    }
+  }
+  .line-chart {
+    position: relative;
+    height: 45%;
+    width: 100%;
+    top: 12%;
+    .title {
+      position: absolute;
+      top: -5%;
+      left: 10px;
+      font-size: 20px;
+    }
+  }
+}
+.switch-icon {
+  position: absolute;
+  color: #cfd3dc;
+  font-size: 16px;
+  z-index: 99;
+  &:hover {
+    font-size: 16px;
+    color: #fff;
+    cursor: pointer;
+  }
+}
+.carousel-dots{
+  position: absolute;
+  bottom: 25px;
+  left: 50%;
+  transform: translate(-50%, 50%);
+}
+</style>

+ 161 - 0
src/views/smartCity/IAQ.vue

@@ -0,0 +1,161 @@
+<template>
+  <div class="content">
+    <div class="title">
+      <a-icon type="double-right" /> IAQ Realtime Sensor Data
+      <a-icon class="close-btn" type="close" @click="closeModal" />
+    </div>
+    <div class="data-list">
+      <div class="data-list-item">
+        <div><span class="iconfont title-icon">&#xe696;</span>CH<sub>2</sub>O</div>
+        <div>
+          <span class="value">0.01</span> mg/m3 <a-icon type="line-chart" class="chart-icon" @click="getData('CH2O')" />
+        </div>
+      </div>
+      <div class="data-list-item">
+        <div><span class="iconfont title-icon">&#xe7e6;</span>CO<sub>2</sub></div>
+        <div>
+          <span class="value">404</span> ppm <a-icon type="line-chart" class="chart-icon" @click="getData('CO2')" />
+        </div>
+      </div>
+      <div class="data-list-item">
+        <div><img src="@/assets/img/smartCity/pm2.5.png" class="title-icon" />PM 2.5</div>
+        <div>
+          <span class="value">14</span> μg/m3 <a-icon type="line-chart" class="chart-icon" @click="getData('PM 2.5')" />
+        </div>
+      </div>
+      <div class="data-list-item">
+        <div><img src="@/assets/img/smartCity/pm10.png" class="title-icon" />PM 10</div>
+        <div>
+          <span class="value">14</span> μg/m3 <a-icon type="line-chart" class="chart-icon" @click="getData('PM 10')" />
+        </div>
+      </div>
+    </div>
+    <div v-if="type" style="height: 100%; width: 100%" ref="lineChart"></div>
+    <div class="empty" v-if="!type">
+      <div><img src="@/assets/img/smartCity/empty.png" alt="" /></div>
+      <div>Click the icon to load the chart data</div>
+    </div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+import echartshelp from './echartshelp'
+import { getOpenPlatformAPI } from '@/api/openPlatform'
+export default {
+  props: ['showModule'],
+  data() {
+    return {
+      type: null,
+      typeMap: {
+        CH2O: '17',
+        CO2: '30',
+        'PM 2.5': 'D8',
+        'PM 10': 'DD',
+      },
+    }
+  },
+  watch: {},
+  async mounted() {},
+  methods: {
+    loadChart(val, arr) {
+      this.type = val
+      this.$nextTick(() => {
+        var myChart = echarts.init(this.$refs.lineChart)
+        let copyOption = JSON.parse(JSON.stringify(echartshelp.lineChartOption))
+        copyOption.title = {
+          text: val,
+          bottom: 25,
+          left: '50%',
+          textAlign: 'center',
+          textStyle: {
+            color: '#cfd3dc',
+          },
+        }
+        copyOption.yAxis.axisLabel.show = true
+        copyOption.series[0].data = arr
+        myChart.setOption(copyOption)
+      })
+    },
+    async getData(type) {
+      const { data } = await getOpenPlatformAPI({
+        path:
+          '/smart/hwmobile/smart/cloudOuter!findDeviceHistory24HourArray?SENSORID=7700C641483E&KEY=ou3eDi0knmf3di&TYPE=' +
+          this.typeMap[type],
+      })
+      if (data) {
+        const arr = data.split(';')[1].split(',')
+        this.loadChart(type, arr)
+      }
+    },
+    closeModal() {
+      this.$emit('update:showModule', null)
+    },
+  },
+}
+</script>
+
+<style scoped lang="less">
+.content {
+  display: flex;
+  flex-direction: column;
+  color: #fff;
+  height: 100%;
+  width: 100%;
+  .title {
+    position: relative;
+    margin: 10px;
+    font-size: 18px;
+    .close-btn {
+      position: absolute;
+      right: 10px;
+      top: 5px;
+    }
+  }
+  .data-list {
+    margin: 18px 36px;
+    background-image: url(~@/assets/img/smartCity/rightBg.png);
+    background-size: 100% 100%;
+    .title-icon {
+      width: 18px;
+      margin-right: 5px;
+    }
+    .chart-icon {
+      margin-left: 5px;
+      color: #1890ff;
+      cursor: pointer;
+    }
+    .data-list-item {
+      padding: 10px 15px;
+      font-size: 18px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      div {
+        display: flex;
+        align-items: center;
+        font-size: 15px;
+        color: #c0c4cc;
+      }
+      .value {
+        color: #fff;
+        font-size: 18px;
+        margin-right: 5px;
+      }
+    }
+  }
+
+  .empty {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+
+    img {
+      height: 100px;
+      width: 100px;
+    }
+  }
+}
+</style>

+ 144 - 0
src/views/smartCity/ThermalComfort.vue

@@ -0,0 +1,144 @@
+<template>
+  <div class="content">
+    <div class="title">
+      <a-icon type="double-right" /> Thermal Comfort Realtime Sensor Data
+      <a-icon class="close-btn" type="close" @click="closeModal" />
+    </div>
+    <div class="data-list">
+      <div class="data-list-item">
+        <div><img src="@/assets/temperature.png" class="icon" />Temperature</div>
+        <div><span>20</span>°C <a-icon type="line-chart" class="chart-icon" @click="getData('temperature')" /></div>
+      </div>
+      <div class="data-list-item">
+        <div><img src="@/assets/humidity.png" class="icon" />Humidity</div>
+        <div><span>36</span>%RH <a-icon type="line-chart" class="chart-icon" @click="getData('Humidity')" /></div>
+      </div>
+    </div>
+    <div v-if="type" style="height: 100%; width: 100%" ref="lineChart"></div>
+    <div class="empty" v-if="!type">
+      <div><img src="@/assets/img/smartCity/empty.png" alt="" /></div>
+      <div>Click the icon to load the chart data</div>
+    </div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+import echartshelp from './echartshelp'
+import { getOpenPlatformAPI } from '@/api/openPlatform'
+export default {
+  props: ['showModule'],
+  data() {
+    return {
+      type: null,
+      typeMap: {
+        temperature: 'C9',
+        Humidity: 'CA',
+      },
+    }
+  },
+  watch: {},
+  async mounted() {},
+  methods: {
+    loadChart(val,arr) {
+      this.$nextTick(() => {
+        var myChart = echarts.init(this.$refs.lineChart)
+        let copyOption = JSON.parse(JSON.stringify(echartshelp.lineChartOption))
+        copyOption.title = {
+          text: val,
+          bottom: 25,
+          left: '50%',
+          textAlign: 'center',
+          textStyle: {
+            color: '#cfd3dc',
+          },
+        }
+        copyOption.yAxis.axisLabel.show = true
+        copyOption.series[0].data = arr
+        myChart.setOption(copyOption)
+      })
+    },
+    async getData(type) {
+      this.type = type
+      const { data } = await getOpenPlatformAPI({
+        path:
+          '/smart/hwmobile/smart/cloudOuter!findDeviceHistory24HourArray?SENSORID=7700C641483E&KEY=ou3eDi0knmf3di&TYPE=' +
+          this.typeMap[type],
+      })
+      if (data) {
+        const arr = data.split(';')[1].split(',')
+        this.loadChart(type,arr)
+      }
+    },
+    closeModal() {
+      this.$emit('update:showModule', null)
+    },
+  },
+}
+</script>
+
+<style scoped lang="less">
+.content {
+  display: flex;
+  flex-direction: column;
+  color: #fff;
+  height: 100%;
+  width: 100%;
+  .title {
+    position: relative;
+    margin: 10px;
+    font-size: 18px;
+    .close-btn {
+      position: absolute;
+      right: 10px;
+      top: 5px;
+    }
+  }
+  .data-list {
+    margin: 18px 36px;
+    background-image: url(~@/assets/img/smartCity/rightBg.png);
+    background-size: 100% 100%;
+    .icon {
+      width: 18px;
+      height: 18px;
+      margin-right: 5px;
+    }
+    .chart-icon {
+      margin-left: 5px;
+      color: #1890ff;
+      cursor: pointer;
+    }
+    .data-list-item {
+      padding: 10px 15px;
+      font-size: 18px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      div {
+        display: flex;
+        align-items: center;
+        font-size: 15px;
+        color: #c0c4cc;
+      }
+      span {
+        color: #fff;
+        font-size: 18px;
+        margin-right: 5px;
+      }
+    }
+  }
+
+  .empty {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+
+    img {
+      height: 100px;
+      width: 100px;
+    }
+  }
+}
+</style>

+ 283 - 0
src/views/smartCity/echartshelp.js

@@ -0,0 +1,283 @@
+import * as echarts from 'echarts'
+
+export default {
+  PieChartOption: {
+    legend: {
+      orient: 'vertical',
+      top: 'middle',
+      right: '6%',
+      itemGap: 10,
+      padding: 10,
+      itemWidth: 15,
+      textStyle: {
+        rich: {
+          a: {
+            color: '#ffffff',
+            width: 60,
+          },
+        },
+      },
+      data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎'],
+    },
+    grid: {
+      width: '40%',
+    },
+    yAxis: {
+      show: false,
+    },
+    xAxis: {
+      show: false,
+    },
+    series: [
+      {
+        type: 'pie',
+        radius: ['40%', '65%'],
+        center: ['25%', '50%'],
+        avoidLabelOverlap: false,
+        left: '5%',
+        label: {
+          normal: {
+            show: true,
+            position: 'center',
+            formatter: ['zhiliang'].join('\n'),
+            rich: {
+              a: {
+                color: '#1AC9FF',
+                fontSize: 22,
+                foneWeight: 'bold',
+                lineHeight: 30,
+              },
+              b: {
+                color: '#ffffff',
+                fontSize: 12,
+              },
+            },
+          },
+        },
+        labelLine: {
+          show: false,
+        },
+        data: [
+          { value: 335, name: '直接访问' },
+          { value: 310, name: '邮件营销' },
+          { value: 234, name: '联盟广告' },
+          { value: 135, name: '视频广告' },
+          { value: 1548, name: '搜索引擎' },
+        ],
+      },
+    ],
+  },
+  lineChartOption: {
+    textStyle: {
+      fontFamily: 'Lato-Regular-15',
+    },
+    title: {
+      textStyle: {
+        fontWeight: 'normal',
+      },
+    },
+    tooltip: {
+      trigger: 'axis',
+    },
+    xAxis: {
+      type: 'category',
+      data: [],
+      axisLabel: {
+        color: '#fff',
+        fontFamily: 'Source-Han-Sans-CN',
+      },
+      axisLine: {
+        lineStyle: {
+          color: 'rgba(85, 170, 255, 0.3)',
+          width: 1,
+        },
+      },
+    },
+    yAxis: {
+      type: 'value',
+      minInterval: 1,
+      axisLabel: {
+        show: false,
+        formatter: '{value} ',
+        textStyle: {
+          color: '#E6E8FA',
+          fontFamily: 'Source-Han-Sans-CN',
+        },
+      },
+      axisLine: {
+        lineStyle: {
+          color: 'rgba(207,211,220,0.1)',
+          width: 2,
+          type: 'solid',
+        },
+      },
+      splitLine: {
+        lineStyle: {
+          color: 'rgba(207,211,220,0.1)',
+          width: 2,
+          type: 'solid',
+        },
+      },
+    },
+    grid: {
+      containLabel: true,
+    },
+    legend: {
+      top: '5%',
+      itemGap: 80,
+      textStyle: {
+        //图例文字的样式
+        color: '#a3a3a3',
+      },
+    },
+    series: [
+      {
+        data: [820, 932, 901, 934, 1290, 1330, 1320],
+        type: 'line',
+        smooth: true,
+        lineStyle: {
+          color: '#00BBFE',
+        },
+        areaStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            {
+              offset: 0,
+              color: 'rgba(29, 235, 255, 0.35)',
+            },
+            {
+              offset: 0.5,
+              color: 'rgba(29, 235, 255, 0.3)',
+            },
+            {
+              offset: 1,
+              color: 'rgba(29, 235, 255, 0)',
+            },
+          ]),
+        },
+        label:{
+          show: true,
+          position: 'top',
+          color:'#fff'
+        }
+      },
+    ],
+  },
+  barChartOption: {
+    legend: {
+      bottom: 10,
+      textStyle: {
+        color: '#E6E8FA',
+      },
+    },
+    grid: {
+      left: '20%',
+      top: 20,
+      bottom: 65,
+    },
+    tooltip: {
+      trigger: 'axis',
+    },
+    xAxis: {
+      type: 'category',
+      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
+      axisLabel: {
+        color: '#fff',
+        fontFamily: 'Source-Han-Sans-CN',
+      },
+      axisLine: {
+        lineStyle: {
+          color: 'rgba(85, 170, 255, 0.3)',
+          width: 1,
+        },
+      },
+    },
+    yAxis: {
+      type: 'value',
+      minInterval: 1,
+      axisLabel: {
+        show: true,
+        formatter: '{value} ',
+        textStyle: {
+          color: '#E6E8FA',
+          fontFamily: 'Source-Han-Sans-CN',
+        },
+      },
+      axisLine: {
+        lineStyle: {
+          color: 'rgba(207,211,220,0.1)',
+          width: 2,
+          type: 'solid',
+        },
+      },
+      splitLine: {
+        lineStyle: {
+          color: 'rgba(207,211,220,0.1)',
+          width: 2,
+          type: 'solid',
+        },
+      },
+    },
+    series: [
+      {
+        name: 'test1',
+        data: [120, -200, 150, 80, -70],
+        type: 'bar',
+        itemStyle: {
+          normal: {
+            color: new echarts.graphic.LinearGradient(
+              0,
+              1,
+              0,
+              0,
+              [
+                {
+                  offset: 0,
+                  color: '#1268f3', // 0% 处的颜色
+                },
+                {
+                  offset: 0.6,
+                  color: '#08a4fa', // 60% 处的颜色
+                },
+                {
+                  offset: 1,
+                  color: '#01ccfe', // 100% 处的颜色
+                },
+              ],
+              false
+            ),
+          },
+        },
+      },
+      {
+        name: 'test2',
+        data: [-120, 200, -150, -80, 70],
+        type: 'bar',
+        itemStyle: {
+          normal: {
+            color: new echarts.graphic.LinearGradient(
+              0,
+              1,
+              0,
+              0,
+              [
+                {
+                  offset: 0,
+                  color: '#31cfd1', // 0% 处的颜色
+                },
+                {
+                  offset: 0.6,
+                  color: '#5fdadb', // 60% 处的颜色
+                },
+                {
+                  offset: 1,
+                  color: '#8de5e6', // 100% 处的颜色
+                },
+              ],
+              false
+            ),
+          },
+        },
+      },
+    ],
+  },
+}

+ 367 - 0
src/views/smartCity/index.vue

@@ -0,0 +1,367 @@
+<template>
+  <div class="main-content" ref="content">
+    <div id="cesiumContainer" class="Container"></div>
+    <div class="TitleCtrl"></div>
+    <div class="header">
+      <div class="title-container">
+        <span class="title-content">
+          <span class="title">Smart City</span>
+        </span>
+      </div>
+      <div class="btns-container">
+        <span class="btns-divider">|</span>
+        <a-dropdown :getPopupContainer="() => $refs.content">
+          <div class="btn">Model List</div>
+          <a-menu slot="overlay">
+            <a-menu-item v-for="modelItem in modelList" :key="modelItem.lightweightName">
+              <a href="javascript:;" @click="locationModel(modelItem.lightweightName)">{{ modelItem.name }}</a>
+            </a-menu-item>
+          </a-menu>
+        </a-dropdown>
+        <span class="btns-divider">|</span>
+        <div class="btn">Model View</div>
+        <span class="btns-divider">|</span>
+        <div @click="modalControl('ESG')" :class="['btn', showModule === 'ESG' ? 'activeMenu' : '']">ESG</div>
+        <span class="btns-divider">|</span>
+        <div
+          @click="modalControl('CarbonAndEnergy')"
+          :class="['btn', showModule === 'CarbonAndEnergy' ? 'activeMenu' : '']"
+        >
+          Carbon & Energy Management
+        </div>
+        <span class="btns-divider">|</span>
+        <div @click="modalControl('IAQ')" :class="['btn', showModule === 'IAQ' ? 'activeMenu' : '']">
+          IAQ Management
+        </div>
+        <span class="btns-divider">|</span>
+        <div
+          @click="modalControl('ThermalComfort')"
+          :class="['btn', showModule === 'ThermalComfort' ? 'activeMenu' : '']"
+        >
+          Thermal Comfort Management
+        </div>
+        <div class="date-container">
+          <div class="time">{{ time.hours }}:{{ time.minute }}:{{ time.second }}</div>
+          <div class="date">{{ time.year }}-{{ time.month }}-{{ time.day }}</div>
+        </div>
+      </div>
+    </div>
+    <div class="ESG" v-if="showModule === 'ESG'"><ESG :showModule.sync="showModule" /></div>
+    <div class="ThermalComfort" v-if="showModule === 'ThermalComfort'">
+      <ThermalComfort :showModule.sync="showModule" />
+    </div>
+    <div class="IAQ" v-if="showModule === 'IAQ'"><IAQ :showModule.sync="showModule" /></div>
+    <div class="CarbonAndEnergy" v-if="showModule === 'CarbonAndEnergy'">
+      <CarbonAndEnergy :showModule.sync="showModule" />
+    </div>
+  </div>
+</template>
+
+<script>
+import { loadModelScript } from '@/utils/util.js'
+import ESG from './ESG'
+import CarbonAndEnergy from './CarbonAndEnergy'
+import IAQ from './IAQ'
+import ThermalComfort from './ThermalComfort'
+export default {
+  components: { ESG, CarbonAndEnergy, IAQ, ThermalComfort },
+  data() {
+    return {
+      modelList: [], //模型列表
+      showModule: null,
+      time: {
+        year: '0',
+        month: '0',
+        day: '0',
+        hours: '0',
+        minute: '0',
+        second: '0',
+      },
+    }
+  },
+  async mounted() {
+    await this.loadModelScript()
+    let data = localStorage.getItem(this.$route.query.id)
+    let list = JSON.parse(data)
+    this.modelList = list
+    this.$store.commit('SET_VIEWSINGLEMODEL', this.modelList)
+    this.$store.commit('SET_MAINMODELMSG', this.modelList[0])
+    this.InitScene()
+    this.getDate()
+  },
+  methods: {
+    loadModelScript,
+    async InitScene() {
+      const copyDefaults = JSON.parse(JSON.stringify(this.$store.state.model.defaults))
+      copyDefaults.openterrain = true
+      copyDefaults.openearth = true
+      copyDefaults.secretkey = this.$store.state.model.mainModelMsg.stationToken
+      this.$store.commit('SET_DEFAULTS', copyDefaults)
+
+      window.api = new window.API(this.$store.state.model.defaults) //api对象后面调用接口要全程使用,控制好作用域
+      window.api.Public.setSceneRenderState(false) //实时渲染
+      this.startTime = new Date()
+      await this.addModel([...this.modelList])
+      window.api.Model.offset(100, 100, 100, '5580063043121581866')
+    },
+    addModel(data) {
+      let that = this
+      if (data.length == 0) {
+        if (this.addNum == this.modelList.length) {
+          this.loadingCompleted = true
+        }
+        return
+      }
+      var modelInfo = data.shift()
+      if (modelInfo.modelType == 'gis' && (modelInfo.fileOwnership == 'dom' || modelInfo.fileOwnership == 'dem')) {
+        fetch(data.modelAccessAddress + '/' + data.lightweightName + '.json')
+          .then((response) => response.json())
+          .then((jsonData) => {
+            if (data.fileOwnership == 'dem') {
+              window.api.Public.setTerrainState(true, data.modelAccessAddress)
+              var rectangle = new window.Cesium.Rectangle.fromDegrees(
+                jsonData.rectangle[0],
+                jsonData.rectangle[1],
+                jsonData.rectangle[2],
+                jsonData.rectangle[3]
+              )
+              window.api.viewer.camera.flyTo({
+                destination: rectangle,
+              })
+            } else {
+              window.api.Public.addImageryProvider(data.modelAccessAddress, false, {
+                serverType: 1,
+                rectangle: jsonData.rectangle_radians,
+                maximumLevel: jsonData.maxzoom,
+                flyto: true,
+                id: 'image' + data.lightweightName,
+              })
+            }
+            this.addNum++
+            this.addModel(data)
+          })
+          .catch((error) => {
+            // 处理错误
+            console.error(error)
+          })
+      } else if (modelInfo.modelType == 'cad') {
+        let that = this
+        window.api.Cad.Drawing.load(modelInfo.modelAccessAddress, modelInfo.lightweightName, {
+          flyto: true, //是否飞到图纸跟前
+          visualRange: 100000,
+          isShow3DDrawing: that.modelList.length > 1 ? true : false, // 是否以3D模式渲染,默认false (2D)
+          readyFunc: (res) => {
+            console.log('模型开始加载 回调:', res)
+          },
+          loadedFunc: (tag, obj) => {
+            that.addNum++
+            that.addModel(data)
+            console.log('loadedFunc 回调:', tag, obj)
+          },
+        })
+      } else {
+        window.api.Model.mergeModel(
+          modelInfo.modelAccessAddress,
+          modelInfo.lightweightName,
+          (res) => {},
+          (res) => {
+            that.addNum++
+            //平移
+            window.api.Model.offset(13, -5, -9, modelInfo.lightweightName)
+            //旋转
+            let modelCenterRotate = window.api.Model.getCenter(modelInfo.lightweightName).position
+            var point2 = [modelCenterRotate[0], modelCenterRotate[1], modelCenterRotate[2] + 0.00000001]
+            var options = {
+              tags: [modelInfo.lightweightName],
+              rotate: -30,
+              point1: modelCenterRotate,
+              point2: point2,
+              type: 0,
+            }
+            window.api.Model.rotateByAnyAxis(options)
+            that.addModel(data)
+          },
+          {
+            flyto: true,
+            osgbEdit: true,
+            maxspaceerror: 0.5,
+            matrix: 'matrix' in modelInfo ? computed(() => Objects(modelInfo.matrix)) : undefined,
+          }
+        )
+      }
+    },
+    modalControl(key) {
+      this.showModule = this.showModule === key ? null : key
+    },
+    locationModel(modelId) {
+      window.api.Model.location(modelId)
+    },
+    getDate() {
+      this.timer = setInterval(() => {
+        const date = new Date()
+        this.time.year = date.getFullYear().toString()
+        this.time.month = date.getMonth().toString()
+        this.time.day = date.getDate().toString()
+        this.time.hours = date.getHours().toString().padStart(2, '0')
+        this.time.minute = date.getMinutes().toString().padStart(2, '0')
+        this.time.second = date.getSeconds().toString().padStart(2, '0')
+      }, 1000)
+    },
+  },
+}
+</script>
+<style scoped lang="less">
+/deep/ .ant-dropdown-menu {
+  background: rgba(58, 68, 87, 0.6);
+  .ant-dropdown-menu-item > a {
+    color: #e6e8fa;
+    &:hover {
+      color: #409eff;
+    }
+  }
+  .ant-dropdown-menu-item:hover {
+    cursor: pointer;
+    background: rgba(0, 0, 0, 0.6);
+    box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
+  }
+}
+
+.main-content {
+  height: 100vh;
+  width: 100vw;
+  position: relative;
+  .Container {
+    height: 100vh;
+    width: 100vw;
+  }
+}
+
+.TitleCtrl {
+  position: absolute;
+  top: 0vh;
+  width: 100vw;
+  height: 10vh;
+  background: linear-gradient(to bottom, rgb(9, 21, 20), rgb(9, 21, 45) 60%, rgba(13, 23, 43, 0));
+}
+
+.header {
+  position: absolute;
+  top: 1vh;
+  left: 1vw;
+  width: 99vw;
+  display: flex;
+  color: #fff;
+  .title-container {
+    background: url(~@/assets/img/smartCity/title.png) no-repeat left top;
+    background-size: 100% 100%;
+    height: 56px;
+    width: 300px;
+
+    .title-content {
+      margin: 0 0 0 40px;
+      height: 75%;
+      display: flex;
+      align-items: center;
+      .title {
+        margin-left: 20px;
+        font-size: 24px;
+        color: #fff;
+        text-shadow: 0 0 10px #409eff, 0 0 20px #409eff, 0 0 30px #409eff, 0 0 40px #409eff;
+      }
+    }
+  }
+  .btns-container {
+    flex: 1;
+    height: 56px;
+    font-size: 16px;
+    display: flex;
+    align-items: center;
+    position: relative;
+    background: url(~@/assets/img/smartCity/boder.gif) no-repeat left top;
+    background-size: 100% 100%;
+    .btns-divider {
+      margin: 0 15px;
+      color: #e6e8fa88;
+    }
+    .btn {
+      background: rgba(58, 68, 87, 0.6);
+      padding: 4px 16px;
+      border-radius: 4px;
+      transform: scale(1);
+      transition: transform 0.3s ease; 
+      &:hover {
+        background: #1a3f66;
+        text-shadow: 0 0 5px #265f99, 0 0 5px #265f99, 0 0 5px #265f99, 0 0 5px #265f99;
+        cursor: pointer;
+        transform: scale(1.05);
+      }
+    }
+    .activeMenu {
+      background: #1a3f66 ;
+      text-shadow: 0 0 5px #265f99, 0 0 5px #265f99, 0 0 5px #265f99, 0 0 5px #265f99;
+      transform: scale(1.05) ;
+    }
+    .date-container {
+      position: absolute;
+      right: 25px;
+      width: 150px;
+      height: 56px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-items: center;
+      .time {
+        font-size: 24px;
+        font-family: 'DS-Digital' !important;
+      }
+      .date {
+        font-size: 14px;
+        position: relative;
+        top: -8px;
+      }
+    }
+  }
+}
+
+
+
+.ESG {
+  position: absolute;
+  top: 15vh;
+  right: 1vw;
+  height: 60vh;
+  width: 23vw;
+  background: rgba(9, 21, 45, 0.5);
+  border-radius: 5px;
+  z-index: 1;
+}
+.ThermalComfort {
+  position: absolute;
+  top: 15vh;
+  right: 1vw;
+  height: 50vh;
+  width: 23vw;
+  background: rgba(9, 21, 45, 0.5);
+  border-radius: 5px;
+  z-index: 1;
+}
+.IAQ {
+  position: absolute;
+  top: 15vh;
+  right: 1vw;
+  height: 60vh;
+  width: 23vw;
+  background: rgba(9, 21, 45, 0.5);
+  border-radius: 5px;
+  z-index: 1;
+}
+.CarbonAndEnergy {
+  position: absolute;
+  top: 15vh;
+  right: 1vw;
+  width: 23vw;
+  background: rgba(9, 21, 45, 0.5);
+  border-radius: 5px;
+  z-index: 1;
+}
+</style>

部分文件因为文件数量过多而无法显示