Переглянути джерело

文件上传调整,监控样式调整

lipenghui 3 місяців тому
батько
коміт
505c57f13f

+ 25 - 2
src/api/pms/stat/index.ts

@@ -57,7 +57,30 @@ export const IotStatApi = {
     return await request.get({ url: `/rq/stat/maintenance/type` })
   },
   getDeviceInfoChart: async (deviceCode: any, identifier: any) => {
-  return await request.get({ url: `/rq/stat/td/chart/`+deviceCode+'/'+identifier })
-},
+    return await request.get({ url: `/rq/stat/td/chart/`+deviceCode+'/'+identifier })
+  },
+
+
+  getDeviceCount: async (params: any) => {
+    return await request.get({ url: `/rq/stat/home/device/count` })
+  },
+  getMaintainCount: async (params: any) => {
+    return await request.get({ url: `/rq/stat/home/maintain/count` })
+  },
+  getMainWorkCount: async (params: any) => {
+    return await request.get({ url: `/rq/stat/home/work/count` })
+  },
+  getInspectCount: async (params: any) => {
+    return await request.get({ url: `/rq/stat/home/inspect/count` })
+  },
+  getDeviceStatusCount: async (params: any) => {
+    return await request.get({ url: `/rq/stat/home/device/status` })
+  },
 
+  getDeviceTypeCount: async (params: any) => {
+    return await request.get({ url: `/rq/stat/home/device/type` })
+  },
+  getDeptCount: async (params: any) => {
+    return await request.get({ url: `/rq/stat/home/dept` })
+  },
 }

BIN
src/assets/imgs/iot/IntegratedSolution.png


+ 3 - 3
src/components/UploadFile/src/UploadFile.vue

@@ -3,12 +3,12 @@
     <el-upload
       ref="uploadRef"
       v-model:file-list="fileList"
-      :action="uploadUrl"
+      :action="uploadUrlPath"
       :auto-upload="autoUpload"
       :before-upload="beforeUpload"
       :disabled="disabled"
       :drag="drag"
-      :http-request="httpRequest"
+      :http-request="httpRequestOnlyPath"
       :limit="props.limit"
       :multiple="props.limit > 1"
       :on-error="excelUploadError"
@@ -95,7 +95,7 @@ const uploadList = ref<UploadUserFile[]>([])
 const fileList = ref<UploadUserFile[]>([])
 const uploadNumber = ref<number>(0)
 
-const { uploadUrl, httpRequest } = useUpload()
+const { uploadUrlPath, httpRequestOnlyPath } = useUpload()
 
 // 文件上传之前判断
 const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {

+ 423 - 142
src/views/Home/Index.vue

@@ -9,12 +9,12 @@
             <Icon icon="ep:menu" class="text-[32px] text-blue-400" />
           </div>
           <span class="text-3xl font-bold text-gray-700">
-            {{ statsData.productCategoryCount }}
+            {{ device.total }}
           </span>
           <el-divider class="my-2" />
           <div class="flex justify-between items-center text-gray-400 text-sm">
             <span>今日新增</span>
-            <span class="text-green-500">+{{ statsData.productCategoryTodayCount }}</span>
+            <span class="text-green-500">+{{ device.today }}</span>
           </div>
         </div>
       </el-card>
@@ -23,14 +23,14 @@
       <el-card class="stat-card" shadow="never">
         <div class="flex flex-col">
           <div class="flex justify-between items-center mb-1">
-            <span class="text-gray-500 text-base font-medium">维修数量</span>
+            <span class="text-gray-500 text-base font-medium">维修工单数量</span>
             <Icon icon="ep:box" class="text-[32px] text-orange-400" />
           </div>
-          <span class="text-3xl font-bold text-gray-700">{{ statsData.productCount }}</span>
+          <span class="text-3xl font-bold text-gray-700">{{ maintain.total }}</span>
           <el-divider class="my-2" />
           <div class="flex justify-between items-center text-gray-400 text-sm">
             <span>今日新增</span>
-            <span class="text-green-500">+{{ statsData.productTodayCount }}</span>
+            <span class="text-green-500">+{{ maintain.today }}</span>
           </div>
         </div>
       </el-card>
@@ -39,14 +39,14 @@
       <el-card class="stat-card" shadow="never">
         <div class="flex flex-col">
           <div class="flex justify-between items-center mb-1">
-            <span class="text-gray-500 text-base font-medium">保养数量</span>
+            <span class="text-gray-500 text-base font-medium">保养工单数量</span>
             <Icon icon="ep:cpu" class="text-[32px] text-purple-400" />
           </div>
-          <span class="text-3xl font-bold text-gray-700">{{ statsData.deviceCount }}</span>
+          <span class="text-3xl font-bold text-gray-700">{{ work.total }}</span>
           <el-divider class="my-2" />
           <div class="flex justify-between items-center text-gray-400 text-sm">
             <span>今日新增</span>
-            <span class="text-green-500">+{{ statsData.deviceTodayCount }}</span>
+            <span class="text-green-500">+{{ work.today }}</span>
           </div>
         </div>
       </el-card>
@@ -55,16 +55,16 @@
       <el-card class="stat-card" shadow="never">
         <div class="flex flex-col">
           <div class="flex justify-between items-center mb-1">
-            <span class="text-gray-500 text-base font-medium">巡检数量</span>
-            <Icon icon="ep:message" class="text-[32px] text-teal-400" />
+            <span class="text-gray-500 text-base font-medium">巡检工单数量</span>
+            <Icon icon="ep:camera" class="text-[32px] text-teal-400" />
           </div>
           <span class="text-3xl font-bold text-gray-700">
-            {{ statsData.deviceMessageCount }}
+            {{ inspect.total }}
           </span>
           <el-divider class="my-2" />
           <div class="flex justify-between items-center text-gray-400 text-sm">
             <span>今日新增</span>
-            <span class="text-green-500">+{{ statsData.deviceMessageTodayCount }}</span>
+            <span class="text-green-500">+{{ inspect.today }}</span>
           </div>
         </div>
       </el-card>
@@ -73,61 +73,76 @@
 
   <!-- 第二行:图表行 -->
   <el-row :gutter="16" class="mb-4">
-    <el-col :span="8">
-      <el-card class="chart-card" shadow="never">
+    <el-col :span="6">
+      <el-card class="stat-card" shadow="never">
         <template #header>
-          <div class="flex items-center">
-            <span class="text-base font-medium text-gray-600">设备状态统计</span>
+          <div class="flex items-center justify-between">
+            <span class="text-base font-medium text-gray-600">MTTR(平均解决时间)</span>
+            <span class="text-base font-medium text-gray-600">库存预警物料数量</span>
           </div>
         </template>
-        <div ref="typeChartRef" class="h-[220px]"></div>
+        <div class="flex flex-col h-[220px]">
+<!--          <div class="flex justify-between items-center text-gray-400">-->
+<!--            <span>MTTR</span>-->
+
+<!--          </div>-->
+<!--          <el-divider />-->
+          <div class="flex justify-between items-center mb-1 mt-15">
+<!--            <span class="text-gray-500 text-base font-medium">平均解决时间</span>-->
+            <!--            <Icon icon="ep:menu" class="text-[32px] text-blue-400" />-->
+            <span class="text-5xl font-bold text-gray-700" style="color: lightseagreen">
+            {{ 4.8 }}
+          </span>
+            <span class="text-5xl font-bold text-gray-700" style="color: indianred">
+            {{ 10 }}
+          </span>
+          </div>
+
+        </div>
       </el-card>
     </el-col>
-    <el-col :span="8">
+    <el-col :span="9">
       <el-card class="chart-card" shadow="never">
         <template #header>
           <div class="flex items-center">
             <span class="text-base font-medium text-gray-600">设备状态统计</span>
           </div>
         </template>
-        <div ref="typeChartRef" class="h-[220px]"></div>
+        <div ref="statusChartRef" class="h-[220px]"></div>
       </el-card>
     </el-col>
-    <el-col :span="8">
+    <el-col :span="9">
       <el-card class="chart-card" shadow="never">
         <template #header>
           <div class="flex items-center">
-            <span class="text-base font-medium text-gray-600">今日工单状态统计</span>
+            <span class="text-base font-medium text-gray-600">设备类别TOP5数量</span>
           </div>
         </template>
-        <el-row class="h-[220px]">
-          <el-col :span="12" class="flex flex-col items-center">
-            <div ref="writeTodayChartRef" class="h-[160px] w-full"></div>
-            <div class="text-center mt-2">
-              <span class="text-sm text-gray-600">待执行</span>
-            </div>
-          </el-col>
-          <el-col :span="12" class="flex flex-col items-center">
-            <div ref="finishedTodayChartRef" class="h-[160px] w-full"></div>
-            <div class="text-center mt-2">
-              <span class="text-sm text-gray-600">已执行</span>
-            </div>
-          </el-col>
-        </el-row>
+        <div ref="topContainer" class="h-[220px]"></div>
       </el-card>
     </el-col>
   </el-row>
 
   <!-- 第三行:消息统计行 -->
-  <el-row>
-    <el-col :span="24">
+  <el-row :gutter="16" class="mb-4">
+    <el-col :span="6">
+      <el-card class="chart-card" shadow="never">
+        <template #header>
+          <div class="flex items-center justify-between">
+            <span class="text-base font-medium text-gray-600">近一周用户活跃度</span>
+          </div>
+        </template>
+        <div ref="activeDom" class="h-[300px]"></div>
+      </el-card>
+    </el-col>
+    <el-col :span="18">
       <el-card class="chart-card" shadow="never">
         <template #header>
           <div class="flex items-center justify-between">
-            <span class="text-base font-medium text-gray-600">近一年数量统计</span>
+            <span class="text-base font-medium text-gray-600">近一年工单数量统计</span>
           </div>
         </template>
-        <div ref="chartContainer" class="h-[300px]"></div>
+        <div ref="qxRef" class="h-[300px]"></div>
       </el-card>
     </el-col>
   </el-row>
@@ -137,8 +152,7 @@
 
 <script setup lang="ts" name="Index">
 import * as echarts from 'echarts/core'
-import { BarChart } from 'echarts/charts'; // 显式导入柱状图模块
-
+import { BarChart, GaugeChart, LineChart, PieChart } from 'echarts/charts' // 显式导入柱状图模块
 import {
   GridComponent,
   LegendComponent,
@@ -146,9 +160,9 @@ import {
   ToolboxComponent,
   TooltipComponent
 } from 'echarts/components'
-import { GaugeChart, LineChart, PieChart } from 'echarts/charts'
 import { LabelLayout, UniversalTransition } from 'echarts/features'
 import { CanvasRenderer } from 'echarts/renderers'
+import { useElementSize } from '@vueuse/core'
 import {
   IotStatisticsDeviceMessageSummaryRespVO,
   IotStatisticsSummaryRespVO
@@ -184,12 +198,8 @@ const queryParams = reactive({
   startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 设置默认开始时间为 7 天前
   endTime: Date.now() // 设置默认结束时间为当前时间
 })
-
-const typeChartRef = ref() // 设备数量统计的图表
-const reportingChartRef = ref() // 在线设备统计的图表
-const dealFinishedChartRef = ref() // 离线设备统计的图表
-const transOrderChartRef = ref() // 待激活设备统计的图表
-const orderFinishChartRef = ref()
+const backendData = ref([])
+const statusChartRef = ref() // 设备数量统计的图表
 const deviceMessageCountChartRef = ref() // 上下行消息量统计的图表
 const writeChartRef = ref() // 上下行消息量统计的图表
 const finishedChartRef = ref() // 上下行消息量统计的图表
@@ -212,21 +222,21 @@ const statsData = ref<IotStatisticsSummaryRespVO>({
   productCategoryDeviceCounts: {}
 })
 
-const day = ref({
+const device = ref({
   total: undefined,
-  todo: undefined
+  today: undefined
 })
-const week = ref({
+const maintain = ref({
   total: undefined,
-  todo: undefined
+  today: undefined
 })
-const month = ref({
+const work = ref({
   total: undefined,
-  todo: undefined
+  today: undefined
 })
-const total = ref({
+const inspect = ref({
   total: undefined,
-  todo: undefined
+  today: undefined
 })
 
 const status = ref({
@@ -244,78 +254,30 @@ const messageStats = ref<IotStatisticsDeviceMessageSummaryRespVO>({
   downstreamCounts: {}
 })
 
-/** 处理快捷时间范围选择 */
-const handleTimeRangeChange = (timeRange: string) => {
-  const now = Date.now()
-  let startTime: number
-
-  // TODO @super:这个的计算,看看能不能结合 dayjs 简化。因为 1h、24h、7d 感觉是比较标准的。如果没有,抽到 utils/formatTime.ts 作为一个工具方法
-  switch (timeRange) {
-    case '1h':
-      startTime = now - 60 * 60 * 1000
-      break
-    case '24h':
-      startTime = now - 24 * 60 * 60 * 1000
-      break
-    case '7d':
-      startTime = now - 7 * 24 * 60 * 60 * 1000
-      break
-    default:
-      return
-  }
-
-  // 清空日期选择器
-  dateRange.value = null
-
-  // 更新查询参数
-  queryParams.startTime = startTime
-  queryParams.endTime = now
-
-  // 重新获取数据
-  getStats()
-}
-
-/** 处理自定义日期范围选择 */
-const handleDateRangeChange = (value: [Date, Date] | null) => {
-  if (value) {
-    // 清空快捷选项
-    timeRange.value = ''
-
-    // 更新查询参数
-    queryParams.startTime = value[0].getTime()
-    queryParams.endTime = value[1].getTime()
-
-    // 重新获取数据
-    getStats()
-  }
-}
-
 /** 获取统计数据 */
 const getStats = async () => {
   // 获取基础统计数据
-  IotStatApi.getMaintenanceDay().then((res) => {
-    day.value = res
+  IotStatApi.getDeviceCount().then((res) => {
+    device.value = res
   })
-  IotStatApi.getMaintenanceWeek().then((res) => {
-    week.value = res
+  IotStatApi.getMaintainCount().then((res) => {
+    maintain.value = res
   })
-  IotStatApi.getMaintenanceMonth().then((res) => {
-    month.value = res
+  IotStatApi.getMainWorkCount().then((res) => {
+    work.value = res
   })
-  IotStatApi.getMaintenanceTotal().then((res) => {
-    total.value = res
+  IotStatApi.getInspectCount().then((res) => {
+    inspect.value = res
   })
   IotStatApi.getMaintenanceStatus().then((res) => {
-    debugger
     status.value = res
     initCharts()
   })
   IotStatApi.getMaintenanceTodayStatus().then((res) => {
     todayStatus.value = res
-    debugger
     initCharts()
   })
-  IotStatApi.getMaintenanceType().then((res) => {
+  IotStatApi.getDeviceStatusCount().then((res) => {
     typeData.value = res
     initCharts()
   })
@@ -336,7 +298,7 @@ const getStats = async () => {
 /** 初始化图表 */
 const initCharts = () => {
   // 设备数量统计
-  echarts.init(typeChartRef.value).setOption({
+  echarts.init(statusChartRef.value).setOption({
     tooltip: {
       trigger: 'item'
     },
@@ -376,29 +338,29 @@ const initCharts = () => {
     ]
   })
   //待执行
-  initGaugeChart(
-    writeTodayChartRef.value,
-    todayStatus.value.todo === undefined ? 0 : todayStatus.value.todo,
-    '#05b'
-  )
-  //已执行
-  initGaugeChart(
-    finishedTodayChartRef.value,
-    todayStatus.value.finished === undefined ? 0 : todayStatus.value.finished,
-    '#f50'
-  )
-  // 待执行
-  initGaugeChart(
-    writeChartRef.value,
-    status.value.todo === undefined ? 0 : status.value.todo,
-    '#05b'
-  )
-  //已执行
-  initGaugeChart(
-    finishedChartRef.value,
-    status.value.finished === undefined ? 0 : status.value.finished,
-    '#f50'
-  )
+  // initGaugeChart(
+  //   writeTodayChartRef.value,
+  //   todayStatus.value.todo === undefined ? 0 : todayStatus.value.todo,
+  //   '#05b'
+  // )
+  // //已执行
+  // initGaugeChart(
+  //   finishedTodayChartRef.value,
+  //   todayStatus.value.finished === undefined ? 0 : todayStatus.value.finished,
+  //   '#f50'
+  // )
+  // // 待执行
+  // initGaugeChart(
+  //   writeChartRef.value,
+  //   status.value.todo === undefined ? 0 : status.value.todo,
+  //   '#05b'
+  // )
+  // //已执行
+  // initGaugeChart(
+  //   finishedChartRef.value,
+  //   status.value.finished === undefined ? 0 : status.value.finished,
+  //   '#f50'
+  // )
   // 消息量统计
   //initMessageChart()
 }
@@ -591,8 +553,8 @@ const fetchChartData = async () => {
     setTimeout(() => {
       resolve({
         months: generateMonthLabels(),
-        faults: [20,30,100,40,20,50,70,80,60,90,100,100],
-        repairs: [10,30,90,30,10,20,60,50,22,34,70,85],
+        faults: [20, 30, 100, 40, 20, 50, 70, 80, 60, 90, 100, 100],
+        repairs: [10, 30, 90, 30, 10, 20, 60, 50, 22, 34, 70, 85]
       })
     }, 300)
   })
@@ -672,16 +634,335 @@ const initChart = async () => {
 const handleResize = () => {
   chartInstance?.resize()
 }
+
+const topContainer = ref(null)
+let topInstance = null
+// 响应式容器尺寸
+const { width, height } = useElementSize(topContainer)
+// 处理数据(排序+限制5条)
+const processedData = () => {
+  const data = IotStatApi.getDeviceTypeCount()
+  backendData.value = data
+  return [...backendData.value].sort((a, b) => a.value - b.value)
+}
+
+const fetchTop = () => {
+  IotStatApi.getDeviceTypeCount().then((res) => {
+    backendData.value = res
+  })
+}
+// 初始化图表配置
+const getTopOption = () => {
+  // backendData.value = data
+  const data = backendData.value.sort((a, b) => a.value - b.value)
+  return {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: { type: 'shadow' },
+      formatter: (params) => {
+        const item = params[0]
+        return `${item.name}<br/>${item.marker} ${item.value.toLocaleString()}`
+      }
+    },
+    grid: {
+      height: '200px',
+      left: '6%',
+      right: '6%',
+      bottom: '5%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'value',
+      axisLabel: {
+        formatter: (value) => {
+          if (value >= 10000) return `${(value / 10000).toFixed(1)}万`
+          return value.toLocaleString()
+        }
+      }
+    },
+    yAxis: {
+      type: 'category',
+      data: data.map((item) => item.category),
+      axisTick: { show: false },
+      axisLabel: {}
+    },
+    series: [
+      {
+        type: 'bar',
+        data: data.map((item) => item.value),
+        itemStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+            { offset: 0, color: '#83bff6' },
+            { offset: 0.7, color: '#188df0' },
+            { offset: 1, color: '#188df0' }
+          ]),
+          borderRadius: [0, 8, 8, 0]
+        },
+        label: {
+          show: true,
+          position: 'right',
+          formatter: '{@value}',
+          color: '#333',
+          fontWeight: 'bold'
+        },
+        emphasis: {
+          itemStyle: {
+            shadowBlur: 10,
+            shadowColor: 'rgba(0, 0, 0, 0.5)'
+          }
+        }
+      }
+    ]
+  }
+}
+
+// 初始化图表
+const initTopChart = async () => {
+  await IotStatApi.getDeviceTypeCount().then((res) => {
+    backendData.value = res
+  })
+  if (!topContainer.value) return
+  topInstance = echarts.init(topContainer.value)
+  updateTopChart()
+}
+
+// 更新图表
+const updateTopChart = () => {
+  if (!topInstance) return
+  topInstance.setOption(getTopOption())
+}
+
+// 自适应调整
+watch([width, height], () => {
+  topInstance?.resize()
+})
+
+// 监听数据变化
+watch(
+  backendData,
+  () => {
+    updateTopChart()
+  },
+  { deep: true }
+)
+
+const activeDom = ref(null)
+let activeInstance = null
+
+// 模拟后端数据结构
+// const activeData = ref([
+//   { department: '瑞恒兴域', total: 356, active: 278 },
+//   { department: '瑞鹰国际', total: 284, active: 192 },
+//   { department: '四川瑞都', total: 432, active: 325 },
+//   { department: '运营中心', total: 157, active: 89 }
+// ])
+const activeData = ref([
+])
+const initActiveChart = async () => {
+  if (!activeDom.value) return
+  activeData.value = await IotStatApi.getDeptCount()
+  activeInstance = echarts.init(activeDom.value)
+
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: { type: 'shadow' }, //:ml-citation{ref="1,7" data="citationList"}
+      formatter: (params) => `
+        ${params[0].name}<br/>
+        ${params[0].marker} 总人数: ${params[0].value}<br/>
+        ${params[1].marker} 活跃人数: ${params[1].value}
+      `
+    },
+    legend: {
+      data: ['总人数', '活跃人数'],
+      top: 30
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true //:ml-citation{ref="2,7" data="citationList"}
+    },
+    xAxis: {
+      type: 'category',
+      data: activeData.value.map((item) => item.department),
+      axisLabel: {
+        interval: 0,
+        rotate: 0 //:ml-citation{ref="5" data="citationList"}
+      }
+    },
+    yAxis: {
+      type: 'value',
+      name: '人数',
+      splitLine: {
+        show: true,
+        lineStyle: { type: 'dashed' }
+      }
+    },
+    series: [
+      {
+        name: '总人数',
+        type: 'bar',
+        data: activeData.value.map((item) => item.total),
+        itemStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: '#5470c6' },
+            { offset: 1, color: '#83bff6' }
+          ])
+        },
+        barWidth: 30
+      },
+      {
+        name: '活跃人数',
+        type: 'bar',
+        data: activeData.value.map((item) => item.active),
+        itemStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: '#91cc75' },
+            { offset: 1, color: '#e6f4d2' }
+          ])
+        },
+        barWidth: 30
+      }
+    ]
+  }
+
+  activeInstance.setOption(option)
+}
+
+
+
+
+
+const qxRef = ref(null)
+let qxInstance = null
+
+// 生成近12个月份 (包含当年和去年)
+const generateMonths = () => {
+  const months = []
+  const date = new Date()
+  date.setMonth(date.getMonth() + 1, 1) // 从下个月开始倒推
+
+  for (let i = 0; i < 12; i++) {
+    date.setMonth(date.getMonth() - 1)
+    months.push(`${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}`)
+  }
+  return months.reverse()
+}
+
+// 模拟工单数据
+const mockData = () => {
+  const baseDate = generateMonths()
+  return {
+    xAxis: baseDate,
+    series: [
+      { // 维修工单
+        name: '维修工单',
+        data: baseDate.map((_, i) => Math.floor(Math.random() * 181) + 20)
+      },
+      { // 保养工单
+        name: '保养工单',
+        data: baseDate.map((_, i) => Math.floor(Math.random() * 181) + 20)
+      },
+      { // 巡检工单
+        name: '巡检工单',
+        data: baseDate.map((_, i) => Math.floor(Math.random() * 181) + 20)
+      }
+    ]
+  }
+}
+
+const initQxChart = () => {
+  if (!qxRef.value) return
+
+  const chartQxData = mockData()
+
+  qxInstance = echarts.init(qxRef.value)
+
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'cross',
+        label: {
+          backgroundColor: '#6a7985'
+        }
+      }
+    },
+    legend: {
+      data: chartQxData.series.map(item => item.name),
+      top: 30
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      boundaryGap: false,
+      data: chartQxData.xAxis,
+      axisLabel: {
+        formatter: value => value.split('-').join('/') // 显示为 2023/01
+      }
+    },
+    yAxis: {
+      type: 'value',
+      axisLabel: {
+        formatter: '{value}'
+      }
+    },
+    series: chartQxData.series.map((item, index) => ({
+      name: item.name,
+      type: 'line',
+      smooth: true,
+      symbol: 'circle',
+      symbolSize: 8,
+      itemStyle: {
+        color: ['#5470c6', '#91cc75', '#fac858'][index] // 蓝、绿、黄
+      },
+      areaStyle: {
+        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+          { offset: 0, color: 'rgba(84,112,198,0.4)' },
+          { offset: 1, color: 'rgba(84,112,198,0.1)' }
+        ])
+      },
+      data: item.data
+    }))
+  }
+
+  qxInstance.setOption(option)
+}
+
+// 响应式调整
+const resizeQxChart = () => qxInstance?.resize()
+
+
+
+
+
+
+
 /** 初始化 */
 onMounted(() => {
   getStats()
   initChart()
+  initTopChart()
+  initActiveChart()
+  initQxChart()
+  window.addEventListener('resize', resizeQxChart)
+  // fetchTop()
+  window.addEventListener('resize', () => topInstance?.resize())
 })
-onUnmounted(() => {
+onBeforeUnmount(() => {
   chartInstance?.dispose()
+  window.removeEventListener('resize', () => chartInstance?.resize())
+  topInstance?.dispose()
   window.removeEventListener('resize', handleResize)
+  qxInstance?.dispose()
+  window.removeEventListener('resize', resizeQxChart)
 })
 </script>
 
-<style lang="scss" scoped>
-</style>
+<style lang="scss" scoped></style>

+ 7 - 3
src/views/pms/device/DeviceInfo.vue

@@ -4,9 +4,9 @@
       <div style="flex: 1; height: 12em; margin-left: 20px">
         <el-image
           :key="index"
-          :src="formData.picUrl"
+          :src="defaultPicUrl"
           style="width: 35em; height: 12em"
-          @click="imagePreview(formData.picUrl)"
+          @click="imagePreview(defaultPicUrl)"
         />
       </div>
       <div style="flex: 2; height: 12em; margin-top: 23px">
@@ -195,7 +195,7 @@ import BomInfo from "@/views/pms/device/BomInfo.vue";
 import FailureList from "@/views/pms/device/FailureList.vue";
 import MaintainList from "@/views/pms/device/MaintainList.vue";
 import {createImageViewer} from "@/components/ImageViewer";
-
+const defaultPicUrl = ref('http://1.94.244.160:70/admin-api/infra/file/29/get/IntegratedSolution.png') // 默认设备图片
 
 defineOptions({ name: 'DeviceDetailInfo' })
 
@@ -229,6 +229,7 @@ const formData = ref({
   deptName: undefined
 })
 const pics = ref([])
+const imgSrc = ref('')
 /** 获得详情 */
 const getDetail = async () => {
   if (id) {
@@ -242,6 +243,9 @@ const getDetail = async () => {
           list.value = JSON.parse(res.templateJson)
         }
       }
+      if (formData.value.picUrl) {
+        defaultPicUrl.value = formData.value.picUrl
+      }
     } finally {
       formLoading.value = false
     }

+ 3 - 7
src/views/pms/device/monitor/index.vue

@@ -80,7 +80,7 @@
               <div
                 class="absolute top-0 left-0 right-0 h-[50px] pointer-events-none"
                 :class="[
-                  item.ifInline
+                  item.ifInline===3
                     ? 'bg-gradient-to-b from-[#eefaff] to-transparent'
                     : 'bg-gradient-to-b from-[#fff1f1] to-transparent'
                 ]"
@@ -97,16 +97,12 @@
                   <div class="inline-flex items-center">
                     <div
                       class="w-1 h-1 rounded-full mr-1.5"
-                      :class="
-                        item.ifInline
-                          ? 'bg-[var(--el-color-success)]'
-                          : 'bg-[var(--el-color-danger)]'
-                      "
+                      :class="item.ifInline===3? 'bg-[var(--el-color-success)]': 'bg-[var(--el-color-danger)]'"
                     >
                     </div>
                     <el-text
                       class="!text-xs font-bold"
-                      :type="item.ifInline ? 'success' : 'danger'"
+                      :type="item.ifInline===3 ? 'success' : 'danger'"
                     >
                       {{ getDictLabel(DICT_TYPE.IOT_DEVICE_STATUS, item.ifInline) }}
                     </el-text>

+ 14 - 8
src/views/pms/failure/index.vue

@@ -172,11 +172,11 @@
             <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.needHelp" />
           </template>
         </el-table-column>
-        <el-table-column label="是否停机" align="center" prop="ifStop" >
-          <template #default="scope">
-            <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.ifStop" />
-          </template>
-        </el-table-column>
+<!--        <el-table-column label="是否停机" align="center" prop="ifStop" >-->
+<!--          <template #default="scope">-->
+<!--            <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.ifStop" />-->
+<!--          </template>-->
+<!--        </el-table-column>-->
         <el-table-column
           label="故障时间"
           align="center"
@@ -185,9 +185,13 @@
           width="180px"
         />
   <!--      <el-table-column label="故障影响" align="center" prop="failureInfluence" />-->
-        <el-table-column label="故障系统" align="center" prop="failureSystem" />
+<!--        <el-table-column label="故障系统" align="center" prop="failureSystem" />-->
   <!--      <el-table-column label="故障描述" align="center" prop="description" />-->
-        <el-table-column label="图片" align="center" prop="pic" />
+        <el-table-column label="图片" align="center" prop="pic" >
+            <template #default="scope">
+              <el-button v-if="scope.row.pic" link type="primary" @click="openWeb(scope.row.pic)"> <Icon size="19" icon="ep:view" /></el-button>
+            </template>
+        </el-table-column>
   <!--      <el-table-column label="解决办法" align="center" prop="solution" />-->
   <!--      <el-table-column label="备注" align="center" prop="remark" />-->
 <!--        <el-table-column-->
@@ -277,7 +281,9 @@ const exportLoading = ref(false) // 导出的加载中
 const moreQuery = (show) => {
   ifShow.value = show
 }
-
+const openWeb = (url) => {
+  window.open('http://1.94.244.160:8012/onlinePreview?url='+encodeURIComponent(Base64.encode(url)));
+}
 const handleCommand = (command: string, row: IotFailureReportVO) => {
   switch (command) {
     case 'handleDelete':

+ 8 - 4
src/views/pms/inspect/item/IotInspectItemForm.vue

@@ -1,5 +1,5 @@
 <template>
-  <Dialog :title="dialogTitle" v-model="dialogVisible" style="width: 50vw;height: 40vh" >
+  <Dialog :title="dialogTitle" v-model="dialogVisible" style="width: 50vw;min-height: 50vh" >
     <el-form
       ref="formRef"
       :model="formData"
@@ -47,6 +47,11 @@
             <el-input v-model="formData.standard" type="textarea" placeholder="请输入巡检标准" />
           </el-form-item>
         </el-col>
+        <el-col :span="24">
+          <el-form-item label="附件" prop="urls">
+            <UploadFile v-model="formData.urls" :limit="1" :is-show-tip="false" class="min-w-80px" />
+          </el-form-item>
+        </el-col>
         <el-col :span="24">
         <el-form-item label="备注" prop="remark">
           <el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" />
@@ -67,7 +72,6 @@ import {defaultProps, handleTree} from "@/utils/tree";
 import * as ProductClassifyApi from "@/api/pms/productclassify";
 import DeviceList from "@/views/pms/failure/DeviceList.vue";
 import {CACHE_KEY, useCache} from "@/hooks/web/useCache";
-
 /** 巡检项 表单 */
 defineOptions({ name: 'IotInspectItemForm' })
 
@@ -91,6 +95,7 @@ const formData = ref({
   deviceClassifyName: undefined,
   deviceId: undefined,
   deviceName: undefined,
+  urls: undefined,
 })
 const formRules = reactive({
   deviceClassify: [{ required: true, message: '设备类别不能为空', trigger: 'blur' }],
@@ -147,8 +152,7 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
-    const user = wsCache.get(CACHE_KEY.USER)
-    formData.value.deptId = user.user.deptId;
+    debugger
     const data = formData.value as unknown as IotInspectItemVO
     if (formType.value === 'create') {
       await IotInspectItemApi.createIotInspectItem(data)