| New file |
| | |
| | | @font-face { |
| | | font-family: "Source Han Sans CN"; |
| | | src: url("SourceHanSansCN-Regular.otf") format("opentype"); |
| | | font-weight: 400; |
| | | font-style: normal; |
| | | } |
| | | |
| | | @font-face { |
| | | font-family: "Source Han Sans CN"; |
| | | src: url("SourceHanSansCN-Medium.otf") format("opentype"); |
| | | font-weight: 500; |
| | | font-style: normal; |
| | | } |
| | | |
| | | @font-face { |
| | | font-family: "Source Han Sans CN"; |
| | | src: url("SourceHanSansCN-Bold.otf") format("opentype") ; |
| | | font-weight: 700; |
| | | font-style: normal; |
| | | } |
| | | |
| | | |
| | | @font-face { |
| | | font-family: "Segoe UI"; |
| | | src: url("SourceHanSansCN-Regular.otf") format("opentype"); |
| | | font-weight: 400; |
| | | font-style: normal; |
| | | } |
| | | |
| | | @font-face { |
| | | font-family: "Segoe UI"; |
| | | src: url("SourceHanSansCN-Medium.otf") format("opentype"); |
| | | font-weight: 500; |
| | | font-style: normal; |
| | | } |
| | | |
| | | @font-face { |
| | | font-family: "Segoe UI"; |
| | | src: url("SourceHanSansCN-Bold.otf") format("opentype") ; |
| | | font-weight: 700; |
| | | font-style: normal; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /* 额外的标题字体 */ |
| | | @font-face { |
| | | font-family: "YouSheBiaoTiHei"; |
| | | src: url("YouSheBiaoTiHei.TTF") format("truetype"); |
| | | font-weight: normal; |
| | | font-style: normal; |
| | | } |
| New file |
| | |
| | | // 算法仓库 |
| | | import request from '@/axios' |
| | | // 图片列表 |
| | | export const getalgorithmList = (data, params) => { |
| | | return request({ |
| | | url: `/drone-device-core/aiTmp/list`, |
| | | method: 'post', |
| | | data, |
| | | params |
| | | }) |
| | | } |
| | | //机巢查询 |
| | | export const selectDeviceList = data => { |
| | | return request({ |
| | | url: `/drone-device-core/aiTmp/devices`, |
| | | method: 'get', |
| | | data: data, |
| | | }) |
| | | } |
| New file |
| | |
| | | import request from '@/axios' |
| | | // 事件概况总数 |
| | | export const getJobEventTotal = () => { |
| | | return request({ |
| | | url: '/drone-device-core/jobEvent/total', |
| | | method: 'get', |
| | | }) |
| | | } |
| | | // 事件概况分类数 |
| | | export const getJobEventByStatus = data => { |
| | | return request({ |
| | | url: '/drone-device-core/jobEvent/eventByStatus', |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| | | // 行业任务统计 |
| | | export const industryJobNumPieChart = data => { |
| | | return request({ |
| | | url: '/drone-device-core/jobEvent/deviceEventList', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | // 事件任务统计 |
| | | export const jobEventBar = data => { |
| | | return request({ |
| | | url: '/drone-device-core/wayline/waylineJobInfo/jobEventBar', |
| | | method: 'post', |
| | | data: data, |
| | | }) |
| | | } |
| | | // 设备统计 |
| | | export const getStatics = (areaCode) => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/deviceStatistics', |
| | | method: 'post', |
| | | params: { |
| | | areaCode |
| | | }, |
| | | }); |
| | | }; |
| | | // 日历 |
| | | export const getCalen = data => { |
| | | return request({ |
| | | url: '/drone-device-core/wayline/waylineJobInfo/jobEventBar', |
| | | method: 'post', |
| | | data, |
| | | }) |
| | | } |
| New file |
| | |
| | | import * as echarts from 'echarts' |
| | | |
| | | |
| | | /** |
| | | * ECharts自适应容器大小Hook |
| | | * |
| | | * @param {Object} domRef - DOM元素引用对象,需包含value属性指向要挂载的DOM节点 |
| | | * @returns {Object} chart - 包含value属性的对象,value保存ECharts实例引用 |
| | | */ |
| | | const useEchartsResize = (domRef) => { |
| | | // 图表实例容器,用对象包装便于保持引用地址 |
| | | const chart = {value: null} |
| | | |
| | | // 窗口大小变化处理函数 |
| | | const resizeChart = () => { |
| | | chart.value?.resize() |
| | | } |
| | | |
| | | // 组件挂载后初始化逻辑 |
| | | onMounted(() => { |
| | | if (!domRef.value) return |
| | | // 初始化ECharts实例并注册resize监听 |
| | | chart.value = echarts.init(domRef.value) |
| | | window.addEventListener('resize', resizeChart) |
| | | }) |
| | | |
| | | // 组件卸载前清理逻辑 |
| | | onBeforeUnmount(() => { |
| | | // 移除监听并清理图表实例 |
| | | window.removeEventListener('resize', resizeChart) |
| | | chart.value?.dispose() |
| | | chart.value = null |
| | | }) |
| | | |
| | | return {chart} |
| | | } |
| | | |
| | | export default useEchartsResize |
| New file |
| | |
| | | /* |
| | | * @Author: GuLiMmo 2820890765@qq.com |
| | | * @Date: 2024-06-12 13:51:57 |
| | | * @LastEditors: GuLiMmo 2820890765@qq.com |
| | | * @LastEditTime: 2024-06-12 13:54:00 |
| | | * @FilePath: /bigScreen/src/utils/coordinateTransformation.js |
| | | * @Description: |
| | | * Copyright (c) 2024 by GuLiMmo, All Rights Reserved. |
| | | */ |
| | | const PI = Math.PI; |
| | | const a = 6378245.0; |
| | | const ee = 0.00669342162296594323; |
| | | |
| | | export function wgs84ToGcj02(lng, lat) { |
| | | if (out_of_china(lng, lat)) { |
| | | return [lng, lat]; |
| | | } else { |
| | | var dlat = transformlat(lng - 105.0, lat - 35.0); |
| | | var dlng = transformlng(lng - 105.0, lat - 35.0); |
| | | var radlat = (lat / 180.0) * PI; |
| | | var magic = Math.sin(radlat); |
| | | magic = 1 - ee * magic * magic; |
| | | var sqrtmagic = Math.sqrt(magic); |
| | | dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI); |
| | | dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI); |
| | | var mglat = lat + dlat; |
| | | var mglng = lng + dlng; |
| | | return [mglng, mglat]; |
| | | } |
| | | } |
| | | |
| | | export function gcj02ToWgs84(lng, lat) { |
| | | if (out_of_china(lng, lat)) { |
| | | return [lng, lat]; |
| | | } else { |
| | | var dlat = transformlat(lng - 105.0, lat - 35.0); |
| | | var dlng = transformlng(lng - 105.0, lat - 35.0); |
| | | var radlat = (lat / 180.0) * PI; |
| | | var magic = Math.sin(radlat); |
| | | magic = 1 - ee * magic * magic; |
| | | var sqrtmagic = Math.sqrt(magic); |
| | | dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI); |
| | | dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI); |
| | | var mglat = lat + dlat; |
| | | var mglng = lng + dlng; |
| | | return [lng * 2 - mglng, lat * 2 - mglat]; |
| | | } |
| | | } |
| | | |
| | | function transformlat(lng, lat) { |
| | | var ret = |
| | | -100.0 + |
| | | 2.0 * lng + |
| | | 3.0 * lat + |
| | | 0.2 * lat * lat + |
| | | 0.1 * lng * lat + |
| | | 0.2 * Math.sqrt(Math.abs(lng)); |
| | | ret += |
| | | ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * |
| | | 2.0) / |
| | | 3.0; |
| | | ret += |
| | | ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * |
| | | 2.0) / |
| | | 3.0; |
| | | ret += |
| | | ((160.0 * Math.sin((lat / 12.0) * PI) + |
| | | 320 * Math.sin((lat * PI) / 30.0)) * |
| | | 2.0) / |
| | | 3.0; |
| | | return ret; |
| | | } |
| | | |
| | | function transformlng(lng, lat) { |
| | | var ret = |
| | | 300.0 + |
| | | lng + |
| | | 2.0 * lat + |
| | | 0.1 * lng * lng + |
| | | 0.1 * lng * lat + |
| | | 0.1 * Math.sqrt(Math.abs(lng)); |
| | | ret += |
| | | ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * |
| | | 2.0) / |
| | | 3.0; |
| | | ret += |
| | | ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * |
| | | 2.0) / |
| | | 3.0; |
| | | ret += |
| | | ((150.0 * Math.sin((lng / 12.0) * PI) + |
| | | 300.0 * Math.sin((lng / 30.0) * PI)) * |
| | | 2.0) / |
| | | 3.0; |
| | | return ret; |
| | | } |
| | | |
| | | // 判断是否在国内,不在国内则不做偏移 |
| | | function out_of_china(lng, lat) { |
| | | return ( |
| | | lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271 || false |
| | | ); |
| | | } |
| New file |
| | |
| | | <template> |
| | | <basic-container> |
| | | <div class="algorithContainer"> |
| | | <div class="algorithItem" v-if="!showDetail"> |
| | | <div |
| | | class="item" |
| | | v-for="(item, index) in AlgorithmData" |
| | | :key="index" |
| | | :class="{ 'active-bg': activeItem === item.dictValue }" |
| | | @click="jumpDatail(item)" |
| | | > |
| | | <img class="imgicon" :src="`${baseUrl}/后台-算法仓库/${item.dictValue}.png`" alt="" /> |
| | | <div>{{ item.dictValue }}</div> |
| | | <div |
| | | :class="!statusSign && item.dictValue === nameSign ? 'stopStatus' : 'normalStatus'" |
| | | @click.stop="changeStatus(item)" |
| | | > |
| | | {{ !statusSign && item.dictValue === nameSign ? '停用状态' : '正常状态' }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- 详情页 --> |
| | | <div class="algorithItemDetail" v-else> |
| | | <div class="search"> |
| | | <div class="searchBox"> |
| | | <div class="item"> |
| | | <div class="itemchild">模糊查询:</div> |
| | | <el-input |
| | | v-model="params.name" |
| | | class="filter-item" |
| | | placeholder="请输入事件名称" |
| | | clearable |
| | | ></el-input> |
| | | </div> |
| | | <div class="item"> |
| | | <el-date-picker |
| | | type="daterange" |
| | | range-separator="至" |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | value-format="YYYY-MM-DD" |
| | | v-model="taskData" |
| | | placeholder="请选择日期" |
| | | @change="changeselect" |
| | | /> |
| | | </div> |
| | | <div class="item"> |
| | | <div class="itemchild">机巢查询:</div> |
| | | <el-select v-model="params.device_name" placeholder="请选择" class="filter-item"> |
| | | <el-option v-for="item in jcoptions" :key="item" :label="item" :value="item" /> |
| | | </el-select> |
| | | </div> |
| | | </div> |
| | | <div class="search-btn"> |
| | | <el-button type="primary" icon="el-icon-back" @click="goback">返回</el-button> |
| | | <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button> |
| | | <el-button icon="el-icon-delete" @click="handleReset">清空</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <div |
| | | class="pictureitem" |
| | | v-if="detailData.length > 0" |
| | | v-loading="loading" |
| | | element-loading-text="加载中" |
| | | > |
| | | <div class="imgitem" v-for="(item, index) in detailData" :key="index"> |
| | | <img :src="item.url" alt="" /> |
| | | <div class="info"> |
| | | <div class="name">{{ item.name }}</div> |
| | | <div class="time">{{ item.create_time.slice(5, 16).replace('-', '/', 1) }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <el-empty class="custom-empty" v-else> |
| | | <template #description> |
| | | <span class="custom-text">暂无数据</span> |
| | | </template> |
| | | </el-empty> |
| | | <!-- 分页 --> |
| | | <el-pagination |
| | | class="pageStyle" |
| | | background |
| | | :page-sizes="[10, 20, 30, 50]" |
| | | v-model:current-page="params.current" |
| | | v-model:page-size="params.size" |
| | | layout="total, prev, pager, next,sizes, jumper" |
| | | :total="total" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </basic-container> |
| | | </template> |
| | | |
| | | <script setup> |
| | | defineOptions({ |
| | | name: 'algorithmRepository', |
| | | }); |
| | | import { getDictionaryByCode } from '@/api/system/dictbiz'; |
| | | import { getalgorithmList, selectDeviceList } from '@/api/algorithm'; |
| | | |
| | | import { useRouter } from 'vue-router'; |
| | | const router = useRouter(); |
| | | const baseUrl = import.meta.env.VITE_APP_TERRAIN_URL; |
| | | const showDetail = ref(false); |
| | | const taskData = ref(''); |
| | | const jcvalue = ref(''); |
| | | const jcoptions = ref([]); |
| | | const total = ref(0); |
| | | const loading = ref(true); |
| | | const params = ref({ |
| | | ai_type_key: '', |
| | | start_date: null, |
| | | end_date: null, |
| | | device_name: '', |
| | | name: '', |
| | | current: 1, |
| | | size: 10, |
| | | }); |
| | | // 请求字典字段 |
| | | let AlgorithmData = ref([]); |
| | | const detailData = ref([]); |
| | | const requestDictionary = () => { |
| | | getDictionaryByCode('SF').then(res => { |
| | | if (res.code !== 0) { |
| | | // 处理数据 |
| | | AlgorithmData.value = res.data.data['SF']; |
| | | } |
| | | }); |
| | | }; |
| | | const activeItem = ref(null); |
| | | const jumpDatail = val => { |
| | | showDetail.value = true; |
| | | activeItem.value = val.dictValue; |
| | | params.value.ai_type_key = val.dictKey; |
| | | getList(); |
| | | }; |
| | | const statusSign = ref(true); |
| | | const nameSign = ref(''); |
| | | const changeStatus = val => { |
| | | nameSign.value = val.dictValue; |
| | | statusSign.value = !statusSign.value; |
| | | }; |
| | | // 详情 |
| | | const getList = () => { |
| | | getalgorithmList(params.value).then(res => { |
| | | loading.value = true; |
| | | detailData.value = res.data.data.records; |
| | | total.value = res.data.data.total; |
| | | setTimeout(() => { |
| | | loading.value = false; |
| | | }, 1000); |
| | | }); |
| | | }; |
| | | // 机巢查询 |
| | | const getDeviceList = () => { |
| | | selectDeviceList().then(res => { |
| | | jcoptions.value = res.data.data; |
| | | }); |
| | | }; |
| | | // 日期选择 |
| | | const changeselect = () => { |
| | | params.value.start_date = taskData.value.length ? `${taskData.value[0]} 00:00:00` : null; |
| | | params.value.end_date = taskData.value.length ? `${taskData.value[1]} 23:59:59` : null; |
| | | }; |
| | | const handleSearch = () => { |
| | | getList(); |
| | | }; |
| | | const handleReset = () => { |
| | | params.value.start_date = null; |
| | | params.value.end_date = null; |
| | | params.value.device_name = ''; |
| | | params.value.name = ''; |
| | | getList(); |
| | | }; |
| | | const goback = () => { |
| | | showDetail.value = false; |
| | | activeItem.value = null; |
| | | }; |
| | | // 分页大小改变 |
| | | const handleSizeChange = val => { |
| | | params.size = val; |
| | | getList(); |
| | | }; |
| | | // 页码改变 |
| | | const handleCurrentChange = val => { |
| | | params.current = val; |
| | | getList(); |
| | | }; |
| | | onMounted(() => { |
| | | requestDictionary(); |
| | | getDeviceList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .algorithItem { |
| | | padding: 20px; |
| | | display: grid; |
| | | grid-template-columns: repeat(5, 1fr); |
| | | gap: 53px; |
| | | text-align: center; |
| | | vertical-align: middle; |
| | | .item { |
| | | height: 300px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | background: url('/src/assets/images/ht-sfbg.png') no-repeat center; |
| | | background-size: 100% 100%; |
| | | .imgicon { |
| | | width: 100px; |
| | | height: 100px; |
| | | margin-bottom: 47px; |
| | | } |
| | | &:hover { |
| | | background: url('/src/assets/images/ht-sfbg-hover.png') no-repeat center; |
| | | background-size: 100% 100%; |
| | | } |
| | | &.active-bg { |
| | | background: url('/src/assets/images/ht-sfbg-click.png') no-repeat center; |
| | | background-size: 100% 100%; |
| | | } |
| | | |
| | | .normalStatus { |
| | | width: 116px; |
| | | height: 43px; |
| | | background: #ebfbee; |
| | | border-radius: 50px; |
| | | text-align: center; |
| | | line-height: 43px; |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | color: #029d36; |
| | | margin-top: 23px; |
| | | cursor: pointer; |
| | | } |
| | | .stopStatus { |
| | | width: 116px; |
| | | height: 43px; |
| | | background: #e8e8e8; |
| | | border-radius: 50px 50px 50px 50px; |
| | | text-align: center; |
| | | line-height: 43px; |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | color: #464747; |
| | | margin-top: 23px; |
| | | cursor: pointer; |
| | | } |
| | | .normalStatus:hover { |
| | | background: rgba(6, 217, 87, 0.2); |
| | | } |
| | | } |
| | | } |
| | | .algorithItemDetail { |
| | | padding: 20px; |
| | | |
| | | .pictureitem { |
| | | display: grid; |
| | | grid-template-columns: repeat(5, 1fr); |
| | | gap: 24px; |
| | | |
| | | .imgitem { |
| | | border-radius: 12px 12px 0 0; |
| | | overflow: hidden; |
| | | img { |
| | | width: 100%; |
| | | height: 200px; |
| | | display: block; |
| | | margin: 0; |
| | | padding: 0; |
| | | } |
| | | .info { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | height: 42px; |
| | | border-radius: 0 0 12px 12px; |
| | | overflow: hidden; |
| | | background: linear-gradient(180deg, #ffffff 0%, #e5edff 100%); |
| | | border: 1px solid #1c5cff; |
| | | border-top: none !important; |
| | | |
| | | .name { |
| | | margin-left: 12px; |
| | | font-weight: 500; |
| | | font-size: 16px; |
| | | color: #363636; |
| | | } |
| | | .time { |
| | | margin-right: 12px; |
| | | font-weight: 500; |
| | | font-size: 14px; |
| | | color: #595959; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .search { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | .searchBox { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 27px; |
| | | .itemchild { |
| | | white-space: nowrap; |
| | | margin-right: 5px; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | .item { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-right: 49px; |
| | | } |
| | | } |
| | | .search-btn { |
| | | display: flex; |
| | | } |
| | | .filter-item { |
| | | width: 218px; |
| | | } |
| | | } |
| | | .pageStyle { |
| | | margin-top: 32px; |
| | | display: flex; |
| | | justify-content: right; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="bocklogBox"> |
| | | <div class="block"> |
| | | <div class="title">待办事项</div> |
| | | |
| | | <div class="todo-items"> |
| | | <div |
| | | v-for="(item, index) in todos" |
| | | :key="index" |
| | | class="todo-item" |
| | | :class="`status-${item.status}`" |
| | | > |
| | | <div class="status-indicator"></div> |
| | | |
| | | <div class="content-wrapper"> |
| | | <div class="main-content"> |
| | | <span class="status-tag">{{ statusMap[item.status] }}</span> |
| | | <span class="todo-text">{{ item.title }}</span> |
| | | </div> |
| | | |
| | | <div class="action-area"> |
| | | <img :src="st7" alt=""> |
| | | <span class="todo-date">{{ item.date }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import st7 from '@/assets/images/workbench/st7.png' |
| | | // 状态显示映射 |
| | | const statusMap = { |
| | | pending: '待审核', |
| | | processing: '处理中', |
| | | todo: '待处理', |
| | | }; |
| | | |
| | | // 待办事项数据 |
| | | const todos = ref([ |
| | | { |
| | | status: 'pending', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | { |
| | | status: 'todo', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | { |
| | | status: 'processing', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | { |
| | | status: 'pending', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | { |
| | | status: 'todo', |
| | | title: '发现暴露垃圾事件', |
| | | date: '2025.03.26', |
| | | }, |
| | | ]); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .bocklogBox { |
| | | width: 100%; |
| | | height: 306px; |
| | | background: rgba(255, 255, 255, 0.41); |
| | | box-shadow: 0px 3px 4px -1px rgba(125, 125, 125, 0.25); |
| | | border-radius: 8px 8px 8px 8px; |
| | | border: 2px solid #ffffff; |
| | | .block { |
| | | margin: 11px 21px 13px 22px; |
| | | .title { |
| | | font-weight: bold; |
| | | font-size: 16px; |
| | | color: #363636; |
| | | } |
| | | |
| | | .todo-items { |
| | | display: grid; |
| | | gap: 0.8rem; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .todo-item { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | background: #FFFFFF; |
| | | border-radius: 8px; |
| | | box-shadow: 0px 2px 4px 0px rgba(173, 173, 173, 0.18); |
| | | transition: all 0.2s ease; |
| | | border-left: 4px solid transparent; |
| | | height: 42px; |
| | | &:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 3px 8px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .status-indicator { |
| | | margin-left: 18px; |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .content-wrapper { |
| | | flex: 1; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | gap: 1rem; |
| | | } |
| | | |
| | | .main-content { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 0.8rem; |
| | | |
| | | .status-tag { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | |
| | | padding: 0.3rem 0.5rem; |
| | | border-radius: 12px; |
| | | } |
| | | |
| | | .todo-text { |
| | | font-size: 14px; |
| | | color: #343434; |
| | | } |
| | | } |
| | | |
| | | .action-area { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-right: 42px; |
| | | |
| | | .todo-date { |
| | | color: #666; |
| | | font-size: 0.9rem; |
| | | } |
| | | |
| | | |
| | | } |
| | | } |
| | | |
| | | // 状态颜色方案 |
| | | .status-pending { |
| | | border-left-color: #FF6560; |
| | | color: #FF6560; |
| | | .status-indicator { |
| | | background: #FF6560; |
| | | } |
| | | } |
| | | |
| | | .status-todo { |
| | | border-left-color: #5D77FB; |
| | | color: #5D77FB; |
| | | .status-indicator { |
| | | background: #5D77FB; |
| | | } |
| | | } |
| | | |
| | | .status-processing { |
| | | border-left-color: #FF8B26; |
| | | color: #FF8B26; |
| | | |
| | | .status-indicator { |
| | | background: #FF8B26; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // @media (max-width: 768px) { |
| | | // .todo-list-container { |
| | | // padding: 1rem; |
| | | |
| | | // .content-wrapper { |
| | | // flex-direction: column; |
| | | // align-items: flex-start !important; |
| | | // gap: 0.5rem !important; |
| | | // } |
| | | |
| | | // .action-area { |
| | | // width: 100%; |
| | | // justify-content: space-between; |
| | | // } |
| | | // } |
| | | // } |
| | | |
| | | // @media (max-width: 480px) { |
| | | // .main-content { |
| | | // flex-direction: column; |
| | | // align-items: flex-start !important; |
| | | // } |
| | | |
| | | // .status-tag { |
| | | // margin-bottom: 0.3rem; |
| | | // } |
| | | // } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="calenBox"> |
| | | <el-calendar ref="calendar" v-model="leftValue" @change="handleMonthChange(date)"> |
| | | <template #date-cell="{ data }"> |
| | | <div> |
| | | <div class="date-number">{{ data.day.slice(8, 10) }}</div> |
| | | <div class="events"> |
| | | <div |
| | | v-for="(event, index) in getEvents(data.day)" |
| | | :key="index" |
| | | class="event-item" |
| | | :class="event.type" |
| | | > |
| | | <span></span> |
| | | {{ event.content }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-calendar> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import dayjs from 'dayjs'; |
| | | import { jobEventBar, getCalen } from '@/api/home/index'; |
| | | // 日历事件数据 |
| | | // 格式: 'YYYY-MM-DD': [...events] |
| | | const events = ref({ |
| | | '2025-05-01': [ |
| | | { type: 'work-order', content: '工单26' }, |
| | | { type: 'task', content: '任务26' }, |
| | | ], |
| | | '2025-05-04': [ |
| | | { type: 'task', content: '任务26' }, |
| | | { type: 'work-order', content: '工单26' }, |
| | | ], |
| | | }); |
| | | const params = ref({ |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | |
| | | function getCurrentMonthRange() { |
| | | return { |
| | | start_date: dayjs().startOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | end_date: dayjs().endOf('month').format('YYYY-MM-DD HH:mm:ss'), |
| | | }; |
| | | } |
| | | const leftValue = ref(new Date()); |
| | | watch( |
| | | () => leftValue.value, |
| | | (newV, oldV) => { |
| | | if (newV && dayjs(newV).isSame(dayjs(), 'day')) { |
| | | console.log('点击了今天'); |
| | | } |
| | | |
| | | if (newV && oldV) { |
| | | const newDate = dayjs(newV); |
| | | const oldDate = dayjs(oldV); |
| | | |
| | | // 格式化为 YYYY-MM-DD HH:mm:ss |
| | | const newDateStr = newDate.format('YYYY-MM-DD HH:mm:ss'); |
| | | const oldDateStr = oldDate.format('YYYY-MM-DD HH:mm:ss'); |
| | | params. |
| | | console.log('新日期:', newDateStr, '旧日期:', oldDateStr); |
| | | |
| | | if (newDate.isBefore(oldDate, 'month')) { |
| | | console.log('点击了上个月',newDateStr); |
| | | } else if (newDate.isAfter(oldDate, 'month')) { |
| | | console.log('点击了下个月',newDateStr); |
| | | } |
| | | } |
| | | }, |
| | | { deep: true } |
| | | ); |
| | | const handleMonthChange = date => { |
| | | console.log('date', date); |
| | | |
| | | // 获取当前视图月份的第一天 |
| | | const firstDay = new Date(date.getFullYear(), date.getMonth(), 1); |
| | | |
| | | // 获取当前视图月份的最后一天 |
| | | const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0); |
| | | |
| | | // 格式化为YYYY-MM-DD格式(可选) |
| | | const formatDate = d => d.toISOString().slice(0, 10); |
| | | |
| | | console.log('首日:', formatDate(firstDay)); |
| | | console.log('末日:', formatDate(lastDay)); |
| | | }; |
| | | // 获取日期数字 |
| | | const getDate = date => { |
| | | return date.getDate(); |
| | | }; |
| | | // 获取对应日期的事件 |
| | | const getEvents = dateString => { |
| | | // console.log('000',dateString); |
| | | |
| | | return events.value[dateString] || []; |
| | | }; |
| | | const getJobEventBar = () => { |
| | | const monthRange = getCurrentMonthRange(); |
| | | params.value = monthRange; |
| | | console.log(monthRange); |
| | | getCalen(params.value).then(res => { |
| | | if (res.data.code !== 0) return; |
| | | // events.value = res.data.data |
| | | console.log('日历', res.data.data); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | getJobEventBar(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .calenBox { |
| | | margin-top: 10px; |
| | | height: 546px; |
| | | overflow: hidden; |
| | | .event-item { |
| | | font-size: 12px; |
| | | padding: 2px; |
| | | margin: 2px 0; |
| | | border-radius: 3px; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | |
| | | &.work-order { |
| | | // background: #ecf5ff; |
| | | color: #409eff; |
| | | span { |
| | | display: inline-block; |
| | | width: 7px; |
| | | height: 7px; |
| | | background: #1c5cff; |
| | | border-radius: 50%; |
| | | margin-right: 2px; |
| | | } |
| | | } |
| | | |
| | | &.task { |
| | | // background: #f0f9eb; |
| | | color: #67c23a; |
| | | span { |
| | | display: inline-block; |
| | | width: 7px; |
| | | height: 7px; |
| | | background: #029d36; |
| | | border-radius: 50%; |
| | | margin-right: 2px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="machineNest"> |
| | | <div class="nestTop"> |
| | | <div class="card-title"> |
| | | <img :src="jc1" alt="" /> |
| | | <div class="cardtotal"> |
| | | <p>机巢事件数量排名</p> |
| | | <!-- <div class="total-number">111</div> |
| | | <span>个</span> --> |
| | | </div> |
| | | </div> |
| | | <!-- <div class="status-grid"> |
| | | <div v-for="(item, index) in jcOrder" :key="index" class="status-item"> |
| | | <img :src="jc2" alt="" /> |
| | | <div> |
| | | <div class="status-label">{{ item.name }}</div> |
| | | <div :style="{ color: '#387FC8' }" class="status-value"> |
| | | {{ item.value }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> --> |
| | | </div> |
| | | <div class="nestCenter"> |
| | | <div class="chart" ref="echartsRef"></div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import * as echarts from 'echarts'; |
| | | import useEchartsResize from '@/hooks/useEchartsResize'; |
| | | import jc1 from '@/assets/images/workbench/jc1.png'; |
| | | import jc2 from '@/assets/images/workbench/jc2.png'; |
| | | import { industryJobNumPieChart } from '@/api/home/index'; |
| | | const echartsRef = ref(null); |
| | | let { chart: jcchart } = useEchartsResize(echartsRef); |
| | | const jcOrder = ref([]); |
| | | // 获取机巢事件数据 |
| | | const getIndustryJobNumPieChart = value => { |
| | | industryJobNumPieChart(value).then(res => { |
| | | const resList = res?.data?.data || []; |
| | | jcOrder.value = resList; |
| | | pieInit(resList); |
| | | }); |
| | | }; |
| | | const pieInit = resList => { |
| | | // 处理数据,过滤掉没有name的项 |
| | | const validData = resList.filter(item => item.name); |
| | | |
| | | // 准备图表数据 |
| | | const optionData = { |
| | | yAxisData: validData.map(item => item.name), |
| | | seriesData: validData.map(item => item.value), |
| | | }; |
| | | |
| | | const option = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow', |
| | | }, |
| | | formatter: '{b}: {c}', |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: 'value', |
| | | splitLine: { |
| | | lineStyle: { |
| | | color: '#E5E5E5', |
| | | }, |
| | | }, |
| | | axisLabel: { |
| | | color: '#35455aa6', |
| | | }, |
| | | boundaryGap: [0, 0.01], |
| | | }, |
| | | yAxis: { |
| | | type: 'category', |
| | | data: optionData.yAxisData, // 使用处理后的分类数据 |
| | | axisLabel: { |
| | | color: '#35455aa6', |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#D1D1D1', |
| | | }, |
| | | }, |
| | | axisTick: { |
| | | show: false, |
| | | }, |
| | | }, |
| | | series: [ |
| | | { |
| | | type: 'bar', |
| | | data: optionData.seriesData, // 使用处理后的数值数据 |
| | | itemStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ |
| | | { offset: 0, color: '#93BAFF' }, |
| | | { offset: 1, color: '#C5DEFF' }, |
| | | ]), |
| | | }, |
| | | barWidth: '30%', |
| | | label: { |
| | | show: true, |
| | | position: 'right', |
| | | formatter: '{c}', |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | jcchart.value.setOption(option); |
| | | }; |
| | | onMounted(() => { |
| | | getIndustryJobNumPieChart({ date_enum: 'CURRENT_WEEK' }); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .machineNest { |
| | | .nestTop { |
| | | .card-title { |
| | | display: flex; |
| | | margin-left: 57px; |
| | | margin-bottom: 15px; |
| | | align-items: center; |
| | | img { |
| | | width: 36px; |
| | | height: 40px; |
| | | } |
| | | } |
| | | .cardtotal { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-left: 9px; |
| | | p { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | color: #363636; |
| | | } |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | .total-number { |
| | | font-family: 'YouSheBiaoTiHei'; |
| | | font-weight: bold; |
| | | font-size: 32px; |
| | | color: #2a54ff; |
| | | } |
| | | } |
| | | .status-grid { |
| | | margin-left: 30px; |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | row-gap: 17px; |
| | | .status-item { |
| | | display: flex; |
| | | text-align: center; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: #f6f8fe; |
| | | margin-right: 10px; |
| | | padding: 10px; |
| | | img { |
| | | width: 40px; |
| | | height: 40px; |
| | | margin-right: 13px; |
| | | } |
| | | .status-label { |
| | | font-size: 14px; |
| | | color: #383838; |
| | | } |
| | | .status-value { |
| | | font-family: 'YouSheBiaoTiHei'; |
| | | font-weight: bold; |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .nestCenter { |
| | | width: 100%; |
| | | height: 600px; |
| | | .chart { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="statistics"> |
| | | <div class="title"> |
| | | <div class="name"> |
| | | <span> 设备统计</span> |
| | | <img src="/src/assets/images/workbench/st1.png" alt="" /> |
| | | </div> |
| | | <div class="arrow"> |
| | | <img src="/src/assets/images/workbench/st2.png" alt="" /> |
| | | </div> |
| | | </div> |
| | | <!-- <div class="grid-container"> |
| | | <div v-for="(item, index) in Object.keys(newtitleData)" :key="index" class="device-card"> |
| | | <div class="device-title"> |
| | | <img :src="test[item].img" :alt="newtitleData[item].name" /> |
| | | <div class="itemcenter"> |
| | | <div>{{ test[item]?.name || '--' }}</div> |
| | | <span class="shu">{{ newtitleData[item].total_num }}</span> |
| | | <span>个</span> |
| | | </div> |
| | | </div> |
| | | <div class="status-list"> |
| | | |
| | | <div |
| | | v-for="(status, statusIndex) in newtitleData[item].status_map" |
| | | :key="statusIndex" |
| | | class="status-item" |
| | | :class="getStatusStyle(test[item]?.name, statusIndex)" |
| | | :style="{ color: getStatusColor(test[item]?.name, statusIndex) }" |
| | | > |
| | | <span class="indicator"></span> |
| | | <span class="label">{{ getStatusLabel(test[item]?.name, statusIndex) }}</span> |
| | | <span class="count" |
| | | >{{ status }} {{ test[item]?.name === '无人机' ? '架' : '个' }}</span |
| | | > |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> --> |
| | | <div class="grid-container1"> |
| | | <div v-for="(item, index) in Object.keys(newtitleData)" :key="index" class="device-card"> |
| | | <div class="device-title"> |
| | | <img :src="test[item].img" :alt="newtitleData[item].name" /> |
| | | <div class="itemcenter"> |
| | | <div>{{ test[item]?.name || '--' }}</div> |
| | | <span class="shu">{{ newtitleData[item].total_num }}</span> |
| | | <span>个</span> |
| | | </div> |
| | | </div> |
| | | <div class="status-list"> |
| | | <!-- :class="`status-${statusIndex}`" --> |
| | | <div |
| | | v-for="(status, statusIndex) in newtitleData[item].status_map" |
| | | :key="statusIndex" |
| | | class="status-item" |
| | | :class="getStatusStyle(test[item]?.name, statusIndex)" |
| | | :style="{ color: getStatusColor(test[item]?.name, statusIndex) }" |
| | | > |
| | | <span |
| | | class="indicator" |
| | | :style="{ |
| | | backgroundColor: getStatusBackground(test[item]?.name, statusIndex), |
| | | color: getStatusColor(test[item]?.name, statusIndex), |
| | | }" |
| | | ></span> |
| | | <span class="label">{{ getStatusLabel(test[item]?.name, statusIndex) }}</span> |
| | | <span class="count" |
| | | >{{ status }} {{ test[item]?.name === '无人机' ? '架' : '个' }}</span |
| | | > |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { useStore } from 'vuex'; |
| | | import { computed } from 'vue'; |
| | | import titleImg1 from '@/assets/images/workbench/st3.png'; |
| | | import titleImg2 from '@/assets/images/workbench/st4.png'; |
| | | import titleImg3 from '@/assets/images/workbench/st5.png'; |
| | | import titleImg4 from '@/assets/images/workbench/st6.png'; |
| | | import titleImg5 from '@/assets/images/workbench/st8.png'; |
| | | import titleImg6 from '@/assets/images/workbench/st9.png'; |
| | | import { getStatics } from '@/api/home/index'; |
| | | const store = useStore(); |
| | | Object.key; |
| | | const userInfo = computed(() => store.getters.userInfo); |
| | | const newtitleData = ref({}); |
| | | const test = { |
| | | no_move_list: { |
| | | name: '机巢', |
| | | img: titleImg1, |
| | | }, |
| | | plant_list: { |
| | | name: '无人机', |
| | | img: titleImg2, |
| | | }, |
| | | flow_type_list: { |
| | | name: '机巢流量', |
| | | img: titleImg3, |
| | | }, |
| | | monitor_list: { |
| | | name: '监控设备', |
| | | img: titleImg4, |
| | | }, |
| | | move_list: { |
| | | name: '移动机巢', |
| | | img: titleImg5, |
| | | }, |
| | | |
| | | insure_list: { |
| | | name: '机巢保险', |
| | | img: titleImg6, |
| | | }, |
| | | }; |
| | | const statusSelect = { |
| | | 0: '空闲中', |
| | | 4: '作业中', |
| | | '-1': '离线中', |
| | | }; |
| | | // 流量 状态 0充足,1=流量到期,2=不足 |
| | | const flowStatus = { |
| | | 0: '流量无忧', |
| | | 1: '流量到期', |
| | | 2: '流量不足', |
| | | }; |
| | | // "监控状态 1=在线,0=离线" |
| | | const monitorStatus = { |
| | | 0: '离线中', |
| | | 1: '在线中', |
| | | }; |
| | | // 机巢保险 1=保险,0=未保险 |
| | | const insureStatus = { |
| | | 0: '临近到期', |
| | | 1: '正常期限', |
| | | }; |
| | | // 样式配置对象 |
| | | const statusStyles = { |
| | | 机巢保险: { |
| | | 0: { class: 'expired', color: '#7C8091', background: '#7C8091' }, |
| | | 1: { class: 'normal', color: '#1B94FF', background: '#1B94FF' }, |
| | | }, |
| | | |
| | | 机巢流量: { |
| | | 0: { class: 'offline', color: '#11CE3E', background: '#11CE3E' }, |
| | | 1: { class: 'flying', color: '#1B94FF', background: '#1B94FF' }, |
| | | 2: { class: 'flying', color: '#7C8091', background: '#7C8091' }, |
| | | }, |
| | | 监控设备: { |
| | | 0: { class: 'offline', color: '#bababa', background: '#bababa' }, |
| | | 1: { class: 'flying', color: '#1B94FF', background: '#1B94FF' }, |
| | | }, |
| | | // 默认样式配置 |
| | | default: { |
| | | 0: { class: 'warning', color: '#1b94ff', background: '#1b94ff' }, |
| | | 4: { class: 'success', color: '#11ce3e', background: '#11ce3e' }, |
| | | '-1': { class: 'success', color: '#bababa', background: '#bababa' }, |
| | | }, |
| | | }; |
| | | const getStatusStyle = (name, statusIndex) => { |
| | | // 获取样式配置,优先使用名称专属配置 |
| | | const styleConfig = statusStyles[name] || statusStyles.default; |
| | | return styleConfig[statusIndex]?.class || ''; |
| | | }; |
| | | |
| | | const getStatusColor = (name, statusIndex) => { |
| | | // 获取颜色配置,优先使用名称专属配置 |
| | | const styleConfig = statusStyles[name] || statusStyles.default; |
| | | return styleConfig[statusIndex]?.color || '#333'; |
| | | }; |
| | | // 新增背景颜色获取方法 |
| | | const getStatusBackground = (name, statusIndex) => { |
| | | const styleConfig = statusStyles[name] || statusStyles.default; |
| | | return ( |
| | | styleConfig[statusIndex]?.background || |
| | | styleConfig[statusIndex]?.color || // 降级使用字体颜色 |
| | | '#F0F0F0' |
| | | ); // 最终默认颜色 |
| | | }; |
| | | const getStatusLabel = (itemName, statusCode) => { |
| | | switch (itemName) { |
| | | case '机巢流量': |
| | | return flowStatus[statusCode] || statusSelect[statusCode]; |
| | | case '监控设备': |
| | | return monitorStatus[statusCode] || statusSelect[statusCode]; |
| | | case '机巢保险': |
| | | return insureStatus[statusCode] || statusSelect[statusCode]; |
| | | default: |
| | | return statusSelect[statusCode] || `未知状态(${statusCode})`; |
| | | } |
| | | }; |
| | | const getStaticsList = () => { |
| | | getStatics(userInfo.value.detail.areaCode).then(res => { |
| | | newtitleData.value = res.data.data; |
| | | console.log('设备', newtitleData.value); |
| | | }); |
| | | }; |
| | | |
| | | const unitMap = { |
| | | drone: '架', |
| | | nest: '个', |
| | | monitor: '个', |
| | | mobile: '个', |
| | | }; |
| | | const titleData = ref([ |
| | | { |
| | | img: titleImg1, |
| | | name: '机巢', |
| | | type: 'nest', |
| | | data: 52, |
| | | statuses: [ |
| | | { type: 'working', label: '作业中', count: 20 }, |
| | | { type: 'idle', label: '空间中', count: 20 }, |
| | | { type: 'offline', label: '离线中', count: 20 }, |
| | | ], |
| | | }, |
| | | { |
| | | img: titleImg2, |
| | | name: '无人机', |
| | | data: 52, |
| | | type: 'drone', |
| | | statuses: [ |
| | | { type: 'working', label: '作业中', count: 20 }, |
| | | { type: 'idle', label: '空间中', count: 20 }, |
| | | { type: 'offline', label: '离线中', count: 20 }, |
| | | ], |
| | | }, |
| | | { |
| | | img: titleImg3, |
| | | name: '监控设备', |
| | | type: 'monitor', |
| | | data: 52, |
| | | statuses: [ |
| | | { type: 'working', label: '作业中', count: 20 }, |
| | | { type: 'idle', label: '空间中', count: 20 }, |
| | | { type: 'offline', label: '离线中', count: 20 }, |
| | | ], |
| | | }, |
| | | { |
| | | img: titleImg4, |
| | | name: '移动机巢', |
| | | type: 'mobile', |
| | | data: 52, |
| | | statuses: [ |
| | | { type: 'working', label: '作业中', count: 20 }, |
| | | { type: 'idle', label: '空间中', count: 20 }, |
| | | { type: 'offline', label: '离线中', count: 20 }, |
| | | ], |
| | | }, |
| | | ]); |
| | | onMounted(() => { |
| | | getStaticsList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .statistics { |
| | | // height: 174px; |
| | | background: #ffffff; |
| | | border-radius: 8px 8px 8px 8px; |
| | | margin-bottom: 10px; |
| | | .title { |
| | | padding: 14px 14px 0 21px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | .name { |
| | | display: flex; |
| | | align-items: center; |
| | | span { |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | .arrow { |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | .grid-container { |
| | | padding: 0 14px 0 21px; |
| | | display: grid; |
| | | grid-template-columns: repeat(6, 1fr); |
| | | .device-card { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .device-title { |
| | | display: flex; |
| | | align-items: center; |
| | | img { |
| | | width: 43px; |
| | | height: 44px; |
| | | } |
| | | } |
| | | .itemcenter { |
| | | margin-left: 14px; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #343434; |
| | | .shu { |
| | | font-weight: bold; |
| | | font-size: 36px; |
| | | color: #363636; |
| | | } |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #7c8091; |
| | | } |
| | | } |
| | | } |
| | | .status-list { |
| | | display: grid; |
| | | |
| | | .status-item { |
| | | display: flex; |
| | | align-items: center; |
| | | border-radius: 6px; |
| | | background: #fff; |
| | | font-size: 12px; |
| | | gap: 10px; |
| | | margin-bottom: 5px; |
| | | .indicator { |
| | | width: 6px; |
| | | height: 6px; |
| | | border-radius: 50%; |
| | | } |
| | | .label { |
| | | flex: 1; |
| | | // font-size: clamp(12px, 1vw, 14px); |
| | | } |
| | | |
| | | .count { |
| | | // font-size: clamp(14px, 1.1vw, 16px); |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | &.status-4 .indicator { |
| | | background: #11ce3e; |
| | | } |
| | | &.status-4 { |
| | | color: #11ce3e; |
| | | } |
| | | &.status-0 .indicator { |
| | | background: #1b94ff; |
| | | } |
| | | &.status-0 { |
| | | color: #1b94ff; |
| | | } |
| | | |
| | | &.status--1 .indicator { |
| | | background: #bababa; |
| | | } |
| | | &.status--1 { |
| | | color: #bababa; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .grid-container1 { |
| | | padding: 0 14px 0 21px; |
| | | display: grid; |
| | | grid-template-columns: repeat(6, 1fr); |
| | | .device-card { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .device-title { |
| | | display: flex; |
| | | align-items: center; |
| | | img { |
| | | width: 43px; |
| | | height: 44px; |
| | | } |
| | | } |
| | | .itemcenter { |
| | | margin-left: 14px; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #343434; |
| | | .shu { |
| | | font-weight: bold; |
| | | font-size: 36px; |
| | | color: #363636; |
| | | } |
| | | span { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #7c8091; |
| | | } |
| | | } |
| | | } |
| | | .status-list { |
| | | display: grid; |
| | | |
| | | .status-item { |
| | | display: flex; |
| | | align-items: center; |
| | | border-radius: 6px; |
| | | background: #fff; |
| | | font-size: 12px; |
| | | gap: 10px; |
| | | margin-bottom: 5px; |
| | | .indicator { |
| | | width: 6px; |
| | | height: 6px; |
| | | border-radius: 50%; |
| | | } |
| | | .label { |
| | | flex: 1; |
| | | // font-size: clamp(12px, 1vw, 14px); |
| | | } |
| | | |
| | | .count { |
| | | // font-size: clamp(14px, 1.1vw, 16px); |
| | | font-size: 14px; |
| | | color: #7c8091; |
| | | } |
| | | |
| | | &.status-4 .indicator { |
| | | background: #11ce3e; |
| | | } |
| | | &.status-4 { |
| | | color: #11ce3e; |
| | | } |
| | | &.status-0 .indicator { |
| | | background: #1b94ff; |
| | | } |
| | | &.status-0 { |
| | | color: #1b94ff; |
| | | } |
| | | |
| | | &.status--1 .indicator { |
| | | background: #bababa; |
| | | } |
| | | &.status--1 { |
| | | color: #bababa; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |