| | |
| | | <template> |
| | | <div class="dashboard-container"> |
| | | <hdw-card> |
| | | <div class="data-stats-header"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="8"> |
| | | <div class="user-info-wrapper"> |
| | | <el-avatar class="user-icon" :size="30" :src="userImg" /> |
| | | <span class="user-name">武汉源畅</span> |
| | | <span class="user-change">切换</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="data-stats-time-wrapper"> |
| | | <span class="time-title">统计时间:</span> |
| | | <el-radio-group class="radio-class" v-model="timeType" size="small"> |
| | | <el-radio-button label="本周" :value="0" /> |
| | | <el-radio-button label="本月" :value="1" /> |
| | | <el-radio-button label="本年" :value="2" /> |
| | | </el-radio-group> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="data-stats-time-wrapper"> |
| | | <span class="time-title">自定义时间:</span> |
| | | <el-date-picker |
| | | v-model="rangeTime" |
| | | type="daterange" |
| | | range-separator="至" |
| | | start-placeholder="开始时间" |
| | | end-placeholder="结束时间" |
| | | size="small" |
| | | /> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | <script setup name="Dashboard"> |
| | | import { defineComponent } from 'vue'; |
| | | import HdwCard from '@/components/HdwCard/index.vue'; |
| | | import LedNum from '@/components/LedNum/index.vue'; |
| | | import mapChart from "@/components/echarts/mapChart.vue"; |
| | | import pie from "@/components/echarts/pie.vue"; |
| | | import bar from "@/components/echarts/bar.vue"; |
| | | import groupBar from "@/components/echarts/groupBar.vue"; |
| | | |
| | | </hdw-card> |
| | | <div class="data-stats"> |
| | | <el-row :gutter="8"> |
| | | <el-col :span="8"> |
| | | <hdw-card> |
| | | <div class="data-stats-wrapper"> |
| | | <div class="data-stats-icon"> |
| | | <div class="data-stats-icon-content"> |
| | | <svg-icon icon-class="lock-hdw"/> |
| | | <br/> |
| | | <span class="icon-text">锁具统计</span> |
| | | </div> |
| | | import { ref, watchEffect } from 'vue'; |
| | | import useWebsocket from '@/hooks/useWebsocket'; |
| | | |
| | | const { message: listMessage } = useWebsocket('home'); |
| | | |
| | | const stationNum = ref(0); |
| | | const chartData = ref([]); |
| | | const map = ref(); |
| | | const typePie = ref(); |
| | | const prodPie = ref(); |
| | | const testBar = ref(); |
| | | const useageRateChart = ref(); |
| | | |
| | | let lockDatas = ref([]); |
| | | const rateList = ref([]); |
| | | const rateType = ref('month'); |
| | | |
| | | // "offLineNum": 6, |
| | | // "unLoadNum": 5, |
| | | // "sumLinf": 7, |
| | | // "openNum": 0, |
| | | // "onlineNum": 1, |
| | | // "closeNum": 2 |
| | | |
| | | const offLine_num = ref(0); |
| | | const unLoad_num = ref(0); |
| | | const sumLinf_num = ref(0); |
| | | const open_num = ref(0); |
| | | const online_num = ref(0); |
| | | const close_num = ref(0); |
| | | const rtData = ref([]); |
| | | const alarmData = ref([]); |
| | | |
| | | watchEffect(() => { |
| | | if (listMessage.value) { |
| | | const { resScreenBox, resErrorCtlLog, resAllCtlLog, resLockState, resReport, resAllAinf } = JSON.parse(listMessage.value)?.data; |
| | | console.log('resScreenBox, resErrorCtlLog, resAllCtlLog, resLockState, resReport', resScreenBox, resErrorCtlLog, resAllCtlLog, resLockState, resReport, '============='); |
| | | |
| | | let locks = []; |
| | | let _offLine = 0, |
| | | _unLoad = 0, |
| | | _sumLinf = 0, |
| | | _open = 0, |
| | | _online = 0, |
| | | _close = 0; |
| | | |
| | | if (resLockState.code && resLockState.data) { |
| | | let _list = resLockState.data2; |
| | | locks = _list.allLinfs.filter(v => v.longitude || v.latitude); |
| | | _offLine = _list.offLineNum; |
| | | _unLoad = _list.unLoadNum; |
| | | _sumLinf = _list.sumLinf; |
| | | _open = _list.openNum; |
| | | _online = _list.onlineNum; |
| | | _close = _list.closeNum; |
| | | } |
| | | |
| | | offLine_num.value = _offLine; |
| | | unLoad_num.value = _unLoad; |
| | | sumLinf_num.value = _sumLinf; |
| | | open_num.value = _open; |
| | | online_num.value = _online; |
| | | close_num.value = _close; |
| | | |
| | | lockDatas.value = locks; |
| | | |
| | | if (resAllCtlLog.code && resAllCtlLog.data) { |
| | | let _list = resAllCtlLog.data2.map(v => ({ |
| | | ...v, |
| | | state: v.ctlResult ? '开锁成功' : '开锁失败' |
| | | })); |
| | | rtData.value = _list; |
| | | } |
| | | if (resErrorCtlLog.code && resErrorCtlLog.data) { |
| | | let _list = resErrorCtlLog.data2.map(v => ({ |
| | | ...v, |
| | | state: v.ctlResult ? '开锁成功' : '开锁失败' |
| | | })); |
| | | alarmData.value = _list; |
| | | } |
| | | |
| | | let _rateList = []; |
| | | if (resReport.code && resReport.data) { |
| | | _rateList = resReport.data2; |
| | | } |
| | | rateList.value = _rateList; |
| | | |
| | | |
| | | let area_num = 0; |
| | | if (resAllAinf.code && resAllAinf.data) { |
| | | area_num = resAllAinf.data2; |
| | | } |
| | | |
| | | stationNum.value = area_num; |
| | | |
| | | if (resScreenBox.code && resScreenBox.data) { |
| | | let { product, type } = resScreenBox.data2; |
| | | let prodData = formatPieData(product); |
| | | let typeData = formatPieData(type); |
| | | |
| | | typePie.value.updateChart(typeData.sData); |
| | | prodPie.value.updateChart(prodData.sData); |
| | | } |
| | | |
| | | updateMap(); |
| | | updateStateChart(); |
| | | updateUseageRateChart(); |
| | | } |
| | | }); |
| | | |
| | | function updateStateChart() { |
| | | let labels = ['锁具', '未安装', '开锁', '关锁', '在线', '离线']; |
| | | let datas = [sumLinf_num.value, unLoad_num.value, open_num.value, close_num.value, online_num.value, offLine_num.value]; |
| | | testBar.value.updateChart(labels, datas); |
| | | } |
| | | |
| | | /** |
| | | * 格式化数据 取前4 剩下的为其他 |
| | | */ |
| | | function formatPieData(data) { |
| | | let res = { sData: [] }; |
| | | let arr = Object.keys(data) |
| | | .map((v) => ({ name: v, value: data[v] })) |
| | | .sort((a, b) => { |
| | | return b.value - a.value; |
| | | }); |
| | | let total = 0; |
| | | arr.map(v => { |
| | | total += v.value * 1; |
| | | }); |
| | | |
| | | if (arr.length <= 5) { |
| | | res.sData = arr; |
| | | } else { |
| | | let name = "其他"; |
| | | let value = 0; |
| | | let otherList = []; |
| | | arr.splice(4).forEach((v) => { |
| | | value += v.value * 1; |
| | | let percent = (total ? v.value / total : 0).toFixed(4); |
| | | otherList.push({ |
| | | name: v.name, |
| | | value: v.value, |
| | | percent: (percent * 100).toFixed(2) + "%" |
| | | }); |
| | | }); |
| | | |
| | | |
| | | res.sData = arr; |
| | | res.sData.push({ name, value }); |
| | | } |
| | | return res; |
| | | } |
| | | |
| | | function updateMap() { |
| | | const getColor = (onLine) => ["#aaa", "#0f0"][onLine]; |
| | | let data = lockDatas.value; |
| | | map.value.updateChart( |
| | | data.map((v) => { |
| | | return { |
| | | ...v, |
| | | label: v.lockName, |
| | | color: getColor(v.lockOnline), |
| | | points: [v.longitude, v.latitude], |
| | | // 无实际意义 |
| | | value: 100, |
| | | }; |
| | | }) |
| | | ); |
| | | } |
| | | |
| | | function updateUseageRateChart() { |
| | | // 获取当前的日期对象 |
| | | let currentDate = new Date(); |
| | | // 获取月份数(0 表示一月,11 表示十二月) |
| | | let month = currentDate.getMonth() + 1; |
| | | // 获取季度数 |
| | | let quarter = Math.ceil(month / 3); |
| | | let type = rateType.value; |
| | | let _list = rateList.value; |
| | | let list = []; |
| | | switch (type) { |
| | | case 'month': |
| | | list = _list.map(v => ({ |
| | | label: v.lockName, value: v['month' + month] |
| | | })).sort((a, b) => b.value - a.value); |
| | | break; |
| | | case 'quarter': |
| | | list = _list.map(v => ({ |
| | | label: v.lockName, value: v['quarter' + quarter] |
| | | })).sort((a, b) => b.value - a.value); |
| | | break; |
| | | case 'year': |
| | | list = _list.map(v => ({ |
| | | label: v.lockName, value: v.yearCount |
| | | })).sort((a, b) => b.value - a.value); |
| | | break; |
| | | } |
| | | |
| | | |
| | | let labels = list.slice(0, 5).map(v => v.label); |
| | | let datas = list.slice(0, 5).map(v => v.value); |
| | | |
| | | useageRateChart.value.updateChart(labels, datas); |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="dashboard-wrapper"> |
| | | <div class="dashboard-left"> |
| | | <div class="left-content-list"> |
| | | <div class="left-content-item"> |
| | | <hdw-card is-full title="屏柜类型"> |
| | | <pie ref="typePie"></pie> |
| | | </hdw-card> |
| | | </div> |
| | | <div class="left-content-item"> |
| | | <hdw-card is-full title="设备工作状态"> |
| | | <bar ref="testBar" unit="套"></bar> |
| | | </hdw-card> |
| | | </div> |
| | | <div class="left-content-item last"> |
| | | <hdw-card is-full title="环境状态"> |
| | | <div class="env"> |
| | | <!-- 烟感 --> |
| | | <div class="item"> |
| | | <div class="label">烟感</div> |
| | | <div class="value">0</div> |
| | | </div> |
| | | <div class="data-stats-content"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="12"> |
| | | <div class="num-stats-wrapper"> |
| | | <div class="num-title">已安装</div> |
| | | <div class="num-value">112</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="num-stats-wrapper"> |
| | | <div class="num-title">未安装</div> |
| | | <div class="num-value">2</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="num-stats-wrapper"> |
| | | <div class="num-title">蓝牙锁</div> |
| | | <div class="num-value">112</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="num-stats-wrapper"> |
| | | <div class="num-title">无源锁</div> |
| | | <div class="num-value">2</div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <!-- 温度 --> |
| | | <div class="item"> |
| | | <div class="label">温度</div> |
| | | <div class="value">0</div> |
| | | </div> |
| | | <!-- 湿度 --> |
| | | <div class="item"> |
| | | <div class="label">湿度</div> |
| | | <div class="value">0</div> |
| | | </div> |
| | | </div> |
| | | </hdw-card> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <hdw-card> |
| | | <div class="data-stats-wrapper"> |
| | | <div class="data-stats-icon"> |
| | | <div class="data-stats-icon-content"> |
| | | <svg-icon icon-class="key-hdw"/> |
| | | <br/> |
| | | <span class="icon-text">钥匙统计</span> |
| | | </div> |
| | | </div> |
| | | <div class="data-stats-content"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="12"> |
| | | <div class="num-stats-wrapper"> |
| | | <div class="num-title">实体钥匙</div> |
| | | <div class="num-value">0</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="num-stats-wrapper"> |
| | | <div class="num-title">设备钥匙</div> |
| | | <div class="num-value">0</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="num-stats-wrapper"> |
| | | <div class="num-title">蓝牙钥匙</div> |
| | | <div class="num-value">6</div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | </hdw-card> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <hdw-card is-full> |
| | | <div class="data-stats-wrapper" style="height: 100%"> |
| | | <div class="data-stats-icon"> |
| | | <div class="data-stats-icon-content"> |
| | | <svg-icon icon-class="safe-lock-hdw"/> |
| | | <br/> |
| | | <span class="icon-text">事件统计</span> |
| | | </div> |
| | | </div> |
| | | <div class="data-stats-content"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="12"> |
| | | <div class="num-stats-wrapper"> |
| | | <div class="num-title">开锁操作</div> |
| | | <div class="num-value">0</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="num-stats-wrapper"> |
| | | <div class="num-title">报警事件</div> |
| | | <div class="num-value">0</div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | </hdw-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="data-stats-footer"> |
| | | <el-row :gutter="8" style="height: 100%"> |
| | | <el-col :span="12" style="height: 100%"> |
| | | <hdw-card title="开锁统计" is-full> |
| | | <div class="chart-wrapper"> |
| | | <line-chart :chart-data="unlockingData"></line-chart> |
| | | <div class="dashboard-middle"> |
| | | <div class="middle-content-wrapper"> |
| | | <hdw-card> |
| | | <div class="number-list-wrapper"> |
| | | <div class="number-item"> |
| | | <div class="number-label">区域</div> |
| | | <div class="number-value"> |
| | | <led-num color="#f00" :num="stationNum"></led-num> |
| | | </div> |
| | | </div> |
| | | </hdw-card> |
| | | </el-col> |
| | | <el-col :span="12" style="height: 100%"> |
| | | <hdw-card title="快捷入口" is-full> |
| | | <div class="fast-menu"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="6"> |
| | | <menu-card></menu-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <menu-card url="/system/user" title="用户管理" icon="people-hdw" style="background-color: #56e0c7"></menu-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <menu-card url="/device/lock" title="锁具管理" icon="lock-hdw" style="background-color: #fe945f"></menu-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <menu-card url="/device/key" title="钥匙管理" icon="key-hdw" style="background-color: #fdcc1c"></menu-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <menu-card url="/general/authorize" title="授权管理" icon="auth-hdw" style="background-color: #5a84fd"></menu-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <menu-card url="/general/log" title="日志管理" icon="log-hdw" style="background-color: #7997b3"></menu-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <menu-card url="/general/map" title="地图定位" icon="map-hdw" style="background-color: #1ab5b1"></menu-card> |
| | | </el-col> |
| | | </el-row> |
| | | <div class="number-item"> |
| | | <div class="number-label">锁具</div> |
| | | <div class="number-value"> |
| | | <led-num color="#f00" :num="sumLinf_num"></led-num> |
| | | </div> |
| | | </div> |
| | | <!-- <div class="number-item"> |
| | | <div class="number-label">电子卡</div> |
| | | <div class="number-value"> |
| | | <led-num color="#f00" :num="cardNum"></led-num> |
| | | </div> |
| | | </div> --> |
| | | </div> |
| | | </hdw-card> |
| | | <div class="map-wrapper"> |
| | | <hdw-card is-full> |
| | | <map-chart ref="map" @mapMounted="updateMap"> |
| | | <template #tools> |
| | | <div class="map-mark"> |
| | | <div class="mark online"> |
| | | <el-icon class="ico"><icon-dot /></el-icon>在线 |
| | | <div class="count">{{ online_num }}</div> |
| | | </div> |
| | | <div class="mark offline"> |
| | | <el-icon class="ico"><icon-dot /></el-icon>离线 |
| | | <div class="count">{{ offLine_num }}</div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </map-chart> |
| | | </hdw-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | <div class="dev-real-info"> |
| | | <hdw-card title="实时开锁信息" is-full> |
| | | <el-table :data="rtData" style="width: 100%; height: 100%"> |
| | | <el-table-column prop="ctlTime" label="时间" width="180" /> |
| | | <el-table-column prop="lockId" label="锁具ID" width="180" /> |
| | | <el-table-column prop="lockName" label="锁具名称" /> |
| | | <el-table-column prop="state" label="操作状态" /> |
| | | </el-table> |
| | | </hdw-card> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="dashboard-right"> |
| | | <div class="left-content-list"> |
| | | <div class="left-content-item"> |
| | | <hdw-card is-full title="屏柜品牌"> |
| | | <pie ref="prodPie"></pie> |
| | | </hdw-card> |
| | | |
| | | </div> |
| | | <div class="left-content-item"> |
| | | <hdw-card is-full title="使用频次"> |
| | | <template #tools> |
| | | <el-radio-group v-model="rateType" size="small"> |
| | | <el-radio-button value="month" label="本月"></el-radio-button> |
| | | <el-radio-button value="quarter" label="本季度"></el-radio-button> |
| | | <el-radio-button value="year" label="本年"></el-radio-button> |
| | | </el-radio-group> |
| | | </template> |
| | | <bar ref="useageRateChart" :rotate="30" unit="次"></bar> |
| | | </hdw-card> |
| | | </div> |
| | | <div class="left-content-item last"> |
| | | <hdw-card is-full title="告警统计"> |
| | | <el-table :data="alarmData" style="width: 100%; height: 100%"> |
| | | <el-table-column prop="ctlTime" label="时间" width="160" /> |
| | | <el-table-column prop="lockId" label="锁具ID" width="90" /> |
| | | <el-table-column prop="lockName" label="锁具名称" /> |
| | | <el-table-column prop="state" label="操作状态" /> |
| | | </el-table> |
| | | </hdw-card> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { defineComponent } from 'vue'; |
| | | import MenuCard from '@/views/dashboard/components/MenuCard.vue'; |
| | | import HdwCard from '@/components/HdwCard/index.vue'; |
| | | import SvgIcon from '@/components/SvgIcon/index.vue'; |
| | | import LineChart from './components/LineChart.vue'; |
| | | import userImg from '@/assets/images/user.png'; |
| | | |
| | | export default defineComponent({ |
| | | name: 'Dashboard', |
| | | components: { LineChart, SvgIcon, HdwCard, MenuCard }, |
| | | data() { |
| | | return { |
| | | unlockingData: [], |
| | | userImg, |
| | | timeType: 0, |
| | | rangeTime: '' |
| | | }; |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .dashboard-container { |
| | | height: 100%; |
| | | overflow: auto; |
| | | padding: 8px; |
| | | .data-stats-footer { |
| | | min-height: 300px; |
| | | height: calc(100% - 265px); |
| | | } |
| | | } |
| | | |
| | | .user-info-wrapper { |
| | | display: inline-block; |
| | | .user-icon { |
| | | vertical-align: middle; |
| | | } |
| | | .user-name { |
| | | margin-left: 8px; |
| | | font-size: 14px; |
| | | } |
| | | .user-change { |
| | | margin-left: 8px; |
| | | font-size: 14px; |
| | | color: #409eff; |
| | | font-weight: 700; |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | |
| | | .data-stats-time-wrapper { |
| | | .radio-class { |
| | | vertical-align: middle; |
| | | } |
| | | .time-title { |
| | | margin-right: 8px; |
| | | font-size: 14px; |
| | | display: inline-block; |
| | | } |
| | | } |
| | | |
| | | .data-stats { |
| | | margin-top: 8px; |
| | | padding-bottom: 8px; |
| | | } |
| | | .data-stats-wrapper { |
| | | .dashboard-wrapper { |
| | | display: flex; |
| | | flex-direction: row; |
| | | padding: 16px; |
| | | .data-stats-icon { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | text-align: center; |
| | | flex-direction: column; |
| | | .svg-icon { |
| | | font-size: 100px; |
| | | color: #5e5e5e; |
| | | } |
| | | .icon-text { |
| | | display: inline-block; |
| | | margin-top: 8px; |
| | | font-size: 14px; |
| | | font-weight: 700; |
| | | color: #5e5e5e; |
| | | } |
| | | } |
| | | .data-stats-content { |
| | | flex: 1; |
| | | } |
| | | ; |
| | | } |
| | | .num-stats-wrapper { |
| | | padding: 12px; |
| | | text-align: center; |
| | | .num-title { |
| | | color: gray; |
| | | font-size: 14px; |
| | | } |
| | | .num-value { |
| | | font-size: 30px; |
| | | font-weight: 700; |
| | | } |
| | | } |
| | | .fast-menu { |
| | | text-align: center; |
| | | .el-col { |
| | | padding: 32px 0; |
| | | } |
| | | } |
| | | .chart-wrapper { |
| | | height: 100%; |
| | | overflow: hidden; |
| | | |
| | | .dashboard-left { |
| | | width: 500px; |
| | | height: 100%; |
| | | } |
| | | |
| | | .dashboard-middle { |
| | | flex: 1; |
| | | height: 100%; |
| | | } |
| | | |
| | | .dashboard-right { |
| | | width: 500px; |
| | | height: 100%; |
| | | } |
| | | } |
| | | |
| | | .left-content-list { |
| | | padding: 8px; |
| | | height: 100%; |
| | | |
| | | .left-content-item { |
| | | height: 33%; |
| | | padding-bottom: 8px; |
| | | |
| | | &.last { |
| | | height: 34%; |
| | | padding-bottom: 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .middle-content-wrapper { |
| | | display: flex; |
| | | flex-direction: column; |
| | | padding: 8px 0; |
| | | height: 100%; |
| | | |
| | | .number-list-wrapper { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 0 10px; |
| | | margin-top: 8px; |
| | | |
| | | .number-item { |
| | | cursor: pointer; |
| | | display: flex; |
| | | // border-radius: 6px; |
| | | align-items: center; |
| | | |
| | | .number-label { |
| | | white-space: nowrap; |
| | | background: #0ff; |
| | | border-radius: 6px; |
| | | color: #333; |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .number-value { |
| | | width: 4.5em; |
| | | height: 1.5em; |
| | | padding: 0 10px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .map-wrapper { |
| | | flex: 1; |
| | | padding: 8px 0; |
| | | } |
| | | |
| | | .wrap-chart { |
| | | flex: 1; |
| | | // position: relative; |
| | | } |
| | | |
| | | .map-mark { |
| | | position: absolute; |
| | | right: 10px; |
| | | bottom: 10px; |
| | | |
| | | .mark { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .ico { |
| | | font-size: 14px; |
| | | margin-right: 0.4em; |
| | | } |
| | | |
| | | .count { |
| | | margin-left: 1em; |
| | | } |
| | | |
| | | &.online { |
| | | color: #0f0; |
| | | } |
| | | |
| | | &.offline { |
| | | color: #aaa; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .dev-real-info { |
| | | height: 34%; |
| | | } |
| | | |
| | | } |
| | | |
| | | .env { |
| | | height: 100%; |
| | | padding: 10px; |
| | | display: flex; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | .item { |
| | | flex: 1; |
| | | border-radius: 8px; |
| | | padding: 4px 8px; |
| | | background: #0ff; |
| | | & + .item { |
| | | margin-left: 20px; |
| | | } |
| | | .label { |
| | | color: #333; |
| | | font-weight: bold; |
| | | margin-bottom: 0.4em; |
| | | text-align: center; |
| | | } |
| | | .value { |
| | | background: #000; |
| | | border-radius: 6px; |
| | | padding: 4px 8px; |
| | | color: #fff; |
| | | font-weight: bold; |
| | | text-align: center; |
| | | } |
| | | } |
| | | } |
| | | </style> |