lipenghui 4 settimane fa
parent
commit
d8182a90f9

+ 50 - 47
src/api/pms/inspect/order/detail/index.ts

@@ -1,47 +1,50 @@
-import request from '@/config/axios'
-
-// 巡检工单巡检明细 VO
-export interface IotInspectOrderDetailVO {
-  id: number // 主键id
-  orderId: number // 工单id
-  deviceId: number // 设备id
-  itemId: number // 巡检项id
-  ifNormal: boolean // 是否正常
-  description: string // 异常描述
-  picUrl: string // 图片
-  remark: string // 备注
-  deptId: number // 部门id
-}
-
-// 巡检工单巡检明细 API
-export const IotInspectOrderDetailApi = {
-  // 查询巡检工单巡检明细分页
-  getIotInspectOrderDetailPage: async (params: any) => {
-    return await request.get({ url: `/rq/iot-inspect-order-detail/page`, params })
-  },
-
-  // 查询巡检工单巡检明细详情
-  getIotInspectOrderDetail: async (id: number) => {
-    return await request.get({ url: `/rq/iot-inspect-order-detail/get?id=` + id })
-  },
-
-  // 新增巡检工单巡检明细
-  createIotInspectOrderDetail: async (data: IotInspectOrderDetailVO) => {
-    return await request.post({ url: `/rq/iot-inspect-order-detail/create`, data })
-  },
-
-  // 修改巡检工单巡检明细
-  updateIotInspectOrderDetail: async (data: IotInspectOrderDetailVO) => {
-    return await request.put({ url: `/rq/iot-inspect-order-detail/update`, data })
-  },
-
-  // 删除巡检工单巡检明细
-  deleteIotInspectOrderDetail: async (id: number) => {
-    return await request.delete({ url: `/rq/iot-inspect-order-detail/delete?id=` + id })
-  },
-
-  // 导出巡检工单巡检明细 Excel
-  exportIotInspectOrderDetail: async (params) => {
-    return await request.download({ url: `/rq/iot-inspect-order-detail/export-excel`, params })
-  },
-}
+import request from '@/config/axios'
+
+// 巡检工单巡检明细 VO
+export interface IotInspectOrderDetailVO {
+  id: number // 主键id
+  orderId: number // 工单id
+  deviceId: number // 设备id
+  itemId: number // 巡检项id
+  ifNormal: boolean // 是否正常
+  description: string // 异常描述
+  picUrl: string // 图片
+  remark: string // 备注
+  deptId: number // 部门id
+}
+
+// 巡检工单巡检明细 API
+export const IotInspectOrderDetailApi = {
+  // 查询巡检工单巡检明细分页
+  getIotInspectOrderDetailPage: async (params: any) => {
+    return await request.get({ url: `/rq/iot-inspect-order-detail/page`, params })
+  },
+  getIotInspectItemStatusPage: async (params: any) => {
+    return await request.get({ url: `/rq/iot-inspect-order-detail/item`, params })
+  },
+
+  // 查询巡检工单巡检明细详情
+  getIotInspectOrderDetail: async (id: number) => {
+    return await request.get({ url: `/rq/iot-inspect-order-detail/get?id=` + id })
+  },
+
+  // 新增巡检工单巡检明细
+  createIotInspectOrderDetail: async (data: IotInspectOrderDetailVO) => {
+    return await request.post({ url: `/rq/iot-inspect-order-detail/create`, data })
+  },
+
+  // 修改巡检工单巡检明细
+  updateIotInspectOrderDetail: async (data: IotInspectOrderDetailVO) => {
+    return await request.put({ url: `/rq/iot-inspect-order-detail/update`, data })
+  },
+
+  // 删除巡检工单巡检明细
+  deleteIotInspectOrderDetail: async (id: number) => {
+    return await request.delete({ url: `/rq/iot-inspect-order-detail/delete?id=` + id })
+  },
+
+  // 导出巡检工单巡检明细 Excel
+  exportIotInspectOrderDetail: async (params) => {
+    return await request.download({ url: `/rq/iot-inspect-order-detail/export-excel`, params })
+  },
+}

+ 3 - 0
src/api/pms/stat/index.ts

@@ -38,6 +38,9 @@ export const IotStatApi = {
   getInspectDeviceStatus: async (params: any) => {
     return await request.get({ url: `/rq/stat/inspect/device`, params })
   },
+  getInspectItemStatus: async (params: any) => {
+    return await request.get({ url: `/rq/iot-inspect-order-detail/status`,params })
+  },
   getMaintenanceDay: async (params: any) => {
     return await request.get({ url: `/rq/stat/maintenance/day` })
   },

+ 7 - 1
src/locales/en.ts

@@ -1000,7 +1000,13 @@ export default {
     isException:'IsException',
     exceptionDes:'ExceptionDes',
     inspectTime:'InspectionTime',
-    createName:'creator'
+    createName:'creator',
+    itemNormal:'normalItem',
+    itemException:'exceptionItem',
+    itemNeed:'fillInItem',
+    deviceCount:'deviceCount',
+    exceptionCount:'exceptionDevice',
+    needDevice:'fillInDevice'
   },
   route:{
     RouteName:'RouteName',

+ 7 - 1
src/locales/zh-CN.ts

@@ -995,7 +995,13 @@ export default {
     isException:'是否异常',
     exceptionDes:'异常描述',
     inspectTime:'巡检时间',
-    createName:'创建人'
+    createName:'创建人',
+    itemNormal:'正常巡检项',
+    itemException:'异常巡检项',
+    itemNeed:'待填写巡检项',
+    deviceCount:'应检设备数',
+    exceptionCount:'异常设备数',
+    needDevice:'漏检设备数'
   },
   route:{
     RouteName:'路线名称',

+ 13 - 0
src/router/modules/remaining.ts

@@ -1005,6 +1005,19 @@ const remainingRouter: AppRouteRecordRaw[] = [
           activeMenu: '/inspect/order/index'
         }
       },
+      {
+        path: 'inspect/order/item/index/:deptId?/:status?/:createTime*',
+        component: () => import('@/views/pms/inspect/order/detail/IotInspectOrderItemStat.vue'),
+        name: 'IotInspectItemStat',
+        meta: {
+          noCache: false,
+          hidden: true,
+          canTo: true,
+          icon: 'ep:index',
+          title: t('rem.InspectOrder'),
+          activeMenu: '/inspect/order/item/index'
+        }
+      },
       {
         path: 'inspect/order/add',
         component: () => import('@/views/pms/inspect/order/InspectOrderDetail.vue'),

+ 18 - 27
src/views/Home/Index.vue

@@ -82,22 +82,21 @@
           </div>
         </template>
         <div class="flex flex-col h-[220px]">
-<!--          <div class="flex justify-between items-center text-gray-400">-->
-<!--            <span>MTTR</span>-->
+          <!--          <div class="flex justify-between items-center text-gray-400">-->
+          <!--            <span>MTTR</span>-->
 
-<!--          </div>-->
-<!--          <el-divider />-->
+          <!--          </div>-->
+          <!--          <el-divider />-->
           <div class="flex justify-between items-center mb-1 mt-15">
-<!--            <span class="text-gray-500 text-base font-medium">平均解决时间</span>-->
+            <!--            <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>
+              {{ 4.8 }}
+            </span>
             <span class="text-5xl font-bold text-gray-700" style="color: indianred">
-            {{ safe }}
-          </span>
+              {{ safe }}
+            </span>
           </div>
-
         </div>
       </el-card>
     </el-col>
@@ -756,8 +755,7 @@ let activeInstance = null
 //   { department: '四川瑞都', total: 432, active: 325 },
 //   { department: '运营中心', total: 157, active: 89 }
 // ])
-const activeData = ref([
-])
+const activeData = ref([])
 const initActiveChart = async () => {
   if (!activeDom.value) return
   activeData.value = await IotStatApi.getDeptCount()
@@ -830,10 +828,6 @@ const initActiveChart = async () => {
   activeInstance.setOption(option)
 }
 
-
-
-
-
 const qxRef = ref(null)
 let qxInstance = null
 
@@ -856,15 +850,18 @@ const mockData = () => {
   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)
       }
@@ -890,7 +887,7 @@ const initQxChart = () => {
       }
     },
     legend: {
-      data: chartQxData.series.map(item => item.name),
+      data: chartQxData.series.map((item) => item.name),
       top: 30
     },
     grid: {
@@ -904,7 +901,7 @@ const initQxChart = () => {
       boundaryGap: false,
       data: chartQxData.xAxis,
       axisLabel: {
-        formatter: value => value.split('-').join('/') // 显示为 2023/01
+        formatter: (value) => value.split('-').join('/') // 显示为 2023/01
       }
     },
     yAxis: {
@@ -938,12 +935,6 @@ const initQxChart = () => {
 // 响应式调整
 const resizeQxChart = () => qxInstance?.resize()
 
-
-
-
-
-
-
 /** 初始化 */
 onMounted(() => {
   getStats()

+ 8 - 3
src/views/pms/device/IotDeviceForm.vue

@@ -51,7 +51,7 @@
           <el-col :span="8">
             <el-form-item :label="t('deviceForm.category')" prop="assetClass">
               <el-tree-select
-                :disabled="formType==='update'"
+                :disabled="formType==='update'&&username!=='超级管理员'"
                 v-model="formData.assetClass"
                 :data="productClassifyList"
                 :props="defaultProps"
@@ -370,7 +370,7 @@ import * as ProductClassifyApi from '@/api/pms/productclassify'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 import { useTagsViewStore } from '@/store/modules/tagsView'
 import {DeviceAttrModelApi} from "@/api/pms/deviceattrmodel";
-
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 
 /** 设备台账 表单 */
 defineOptions({ name: 'DeviceDetailAdd' })
@@ -379,12 +379,13 @@ const zzIsExpanded = ref(true) // 控制表单是否展开的变量
 const cwIsExpanded = ref(true) // 控制表单是否展开的变量
 const qtIsExpanded = ref(true) // 控制表单是否展开的变量
 
+const username = ref('')
 const deptList = ref<Tree[]>([]) // 树形结构
 const productClassifyList = ref<Tree[]>([]) // 树形结构
 const { delView } = useTagsViewStore() // 视图操作
 const { params, name } = useRoute() // 查询参数
 const { currentRoute, push } = useRouter()
-
+const { wsCache } = useCache()
 const id = params.id
 const type = params.type
 const deptId = params.deptId
@@ -605,6 +606,10 @@ const submitForm = async () => {
 }
 
 onMounted(async () => {
+  const userInfo = wsCache.get(CACHE_KEY.USER)
+  // NOTE: 是否需要像`setUserInfoAction`一样判断`userInfo != null`
+  username.value = userInfo.user.nickname
+  debugger
   deptList.value = handleTree(await DeptApi.getSimpleDeptList())
   productClassifyList.value = handleTree(
     await ProductClassifyApi.IotProductClassifyApi.getSimpleProductClassifyList()

+ 16 - 1
src/views/pms/inspect/order/InspectOrderDetail.vue

@@ -51,8 +51,23 @@
         <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
           <el-table-column :label="t('iotMaintain.deviceCode')" align="center" prop="deviceCode" />
           <el-table-column :label="t('iotMaintain.deviceName')" align="center" prop="deviceName" />
+          <el-table-column :label="t('inspect.itemNeed')" align="center" prop="itemNeed" width="180" >
+            <template #default="scope">
+              <el-tag  type="info"> {{scope.row.itemNeed}}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('inspect.itemNormal')" align="center" prop="itemNormal" width="180" >
+            <template #default="scope">
+              <el-tag  type="success"> {{scope.row.itemNormal}}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('inspect.itemException')" align="center" prop="itemException" width="180" >
+            <template #default="scope">
+              <el-tag  type="danger"> {{scope.row.itemException}}</el-tag>
+            </template>
+          </el-table-column>
 <!--          <el-table-column label="路线名称" align="center" prop="routeName" />-->
-          <el-table-column :label="t('iotMaintain.operation')" align="center" min-width="120px">
+          <el-table-column :label="t('iotMaintain.operation')" align="center" width="200px">
             <template #default="scope">
               <div style="display: flex; justify-content: center; align-items: center; width: 100%">
                 <div style="margin-right: 10px">

+ 158 - 0
src/views/pms/inspect/order/detail/IotInspectOrderItemStat.vue

@@ -0,0 +1,158 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item :label="t('iotDevice.code')" prop="deviceCode" style="margin-left: 20px">
+        <el-input
+          v-model="queryParams.deviceCode"
+          :placeholder="t('iotDevice.codeHolder')"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-200px"
+        />
+      </el-form-item>
+<!--      <el-form-item :label="t('iotDevice.name')" prop="deviceName">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.deviceName"-->
+<!--          :placeholder="t('iotDevice.nameHolder')"-->
+<!--          clearable-->
+<!--          @keyup.enter="handleQuery"-->
+<!--          class="!w-200px"-->
+<!--        />-->
+<!--      </el-form-item>-->
+      <el-form-item :label="t('bomList.name')" prop="orderName">
+        <el-input
+          v-model="queryParams.orderName"
+          :placeholder="t('bomList.name')"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-200px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery"
+          ><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button
+        >
+        <el-button @click="resetQuery"
+          ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('operationFill.reset') }}</el-button
+        >
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+      <!--      <el-table-column label="主键id" align="center" prop="id" />-->
+      <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
+        <template #default="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column :label="t('bomList.name')" align="center" prop="orderName" />
+      <el-table-column :label="t('iotDevice.code')" align="center" prop="deviceCode" />
+      <el-table-column :label="t('monitor.deviceName')" align="center" prop="deviceName" />
+      <el-table-column :label="t('operationFill.duty')" align="center" prop="charge" />
+      <el-table-column :label="t('inspect.InspectionItems')" align="center" prop="item" />
+      <el-table-column :label="t('inspect.isException')" align="center" prop="ifNormal" >
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.ifNormal" />
+        </template>
+      </el-table-column>
+      <el-table-column :label="t('inspect.exceptionDes')" align="center" prop="description" />
+
+<!--      <el-table-column-->
+<!--        :label="t('common.createTime')"-->
+<!--        align="center"-->
+<!--        prop="createTime"-->
+<!--        width="180px"-->
+<!--      />-->
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+</template>
+
+<script setup lang="ts">
+import {IotInspectItemVO, IotInspectOrderDetailApi} from '@/api/pms/inspect/order/detail'
+import IotInspectOrderDetail from "@/views/pms/inspect/order/detail/index.vue";
+import {DICT_TYPE} from "@/utils/dict";
+
+/** 巡检项 列表 */
+defineOptions({ name: 'IotInspectItemStat' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+const { params } = useRoute()
+const loading = ref(true) // 列表的加载中
+const list = ref<IotInspectItemVO[]>([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const deptId = params.deptId
+const createTime = params.createTime
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  status: undefined,
+  deviceCode: undefined,
+  deviceName: undefined,
+  deptId: undefined,
+  orderName: undefined,
+  createTime: undefined
+})
+const productClassifyList = ref<Tree[]>([]) // 树形结构
+const queryFormRef = ref() // 搜索的表单
+const exportLoading = ref(false) // 导出的加载中
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await IotInspectOrderDetailApi.getIotInspectItemStatusPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 初始化 **/
+onMounted(async () => {
+  debugger
+  if (params.status) {
+    queryParams.status = params.status
+  }
+  if (deptId != null) {
+    queryParams.deptId = deptId
+  }
+
+  if (createTime) {
+    queryParams.createTime = createTime
+  }
+  await getList()
+})
+</script>

+ 16 - 1
src/views/pms/inspect/order/index.vue

@@ -96,7 +96,7 @@
               {{ scope.$index + 1 }}
             </template>
           </el-table-column>
-          <el-table-column :label="t('bomList.name')" align="center" prop="inspectOrderTitle" />
+          <el-table-column :label="t('bomList.name')" align="center" prop="inspectOrderTitle" width="230" />
 <!--          <el-table-column label="工单编码" align="center" prop="inspectOrderCode" />-->
           <el-table-column :label="t('route.orderType')" align="center" prop="type" />
           <el-table-column :label="t('operationFill.status')" align="center" prop="status">
@@ -105,6 +105,21 @@
             </template>
           </el-table-column>
           <el-table-column :label="t('iotMaintain.PersonInCharge')" align="center" prop="chargeName" />
+          <el-table-column :label="t('inspect.deviceCount')" align="center" prop="deviceCount" >
+            <template #default="scope">
+              <el-tag  type="info"> {{scope.row.deviceCount}}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('inspect.needDevice')" align="center" prop="needDevice" >
+            <template #default="scope">
+              <el-tag  type="success"> {{scope.row.needDevice}}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('inspect.exceptionCount')" align="center" prop="exceptionCount" >
+            <template #default="scope">
+              <el-tag  type="danger"> {{scope.row.exceptionCount}}</el-tag>
+            </template>
+          </el-table-column>
 <!--          <el-table-column label="备注" align="center" prop="remark" />-->
           <el-table-column
             label="生成时间"

+ 1 - 1
src/views/pms/maintain/IotMaintain.vue

@@ -26,7 +26,7 @@
               <el-form-item :label="t('iotMaintain.repairType')" prop="type">
                 <el-select v-model="formData.type" :placeholder="t('iotMaintain.repairTypeHolder')" clearable>
                   <el-option
-                    v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAIN_TYPE)"
+                    v-for="dict in getStrDictOptions(DICT_TYPE.d)"
                     :key="dict.value"
                     :label="dict.label"
                     :value="dict.value"

+ 68 - 2
src/views/pms/stat/inspect.vue

@@ -234,8 +234,18 @@
   </el-row>
 
   <!-- 第三行:消息统计行 -->
-  <el-row>
-    <el-col :span="24">
+  <el-row :gutter="16" class="mb-4">
+    <el-col :span="8">
+      <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="statusChartRef" class="h-[300px]"></div>
+      </el-card>
+    </el-col>
+    <el-col :span="16">
       <el-card class="chart-card" shadow="never">
         <template #header>
           <div class="flex items-center justify-between">
@@ -277,6 +287,7 @@ import {DeptTreeItem} from "@/api/system/dept";
 import * as DeptApi from "@/api/system/dept";
 import {useUserStore} from "@/store/modules/user";
 
+
 // TODO @super:参考下 /Users/yunai/Java/yudao-ui-admin-vue3/src/views/mall/home/index.vue,拆一拆组件
 
 /** IoT 首页 */
@@ -319,6 +330,7 @@ const writeChartRef = ref() // 上下行消息量统计的图表
 const finishedChartRef = ref() // 上下行消息量统计的图表
 const writeTodayChartRef = ref() // 上下行消息量统计的图表
 const finishedTodayChartRef = ref() // 上下行消息量统计的图表
+const statusChartRef = ref() // 设备数量统计的图表
 // 基础统计数据
 // TODO @super:初始为 -1,然后界面展示先是加载中?试试用 cursor 改哈
 const statsData = ref<IotStatisticsSummaryRespVO>({
@@ -398,6 +410,7 @@ const todayStatus = ref({
   finished: 0,
   todo: 0
 })
+const typeData = ref({})
 // 消息统计数据
 const messageStats = ref<IotStatisticsDeviceMessageSummaryRespVO>({
   upstreamCounts: {},
@@ -475,6 +488,11 @@ const getStats = async () => {
     initChart()
     initCharts()
   })
+  IotStatApi.getInspectItemStatus(queryParams).then((res) =>{
+    typeData.value = res
+    initChart()
+    initCharts()
+  })
   // statsData.value = await ProductCategoryApi.getIotStatisticsSummary()
 
   //
@@ -486,6 +504,54 @@ const getStats = async () => {
 
 /** 初始化图表 */
 const initCharts = () => {
+  const chart = echarts.init(statusChartRef.value);
+  chart.setOption({
+    tooltip: {
+      trigger: 'item'
+    },
+    legend: {
+      // top: '5%',
+      // right: '10%',
+      // align: 'center',
+      orient: 'horizontal',  // 水平排列图例项
+      bottom: '0%',         // 放置在底部
+      icon: 'circle'
+    },
+    series: [
+      {
+        name: '',
+        type: 'pie',
+        radius: ['0%', '70%'],
+        avoidLabelOverlap: false,
+        center: ['50%', '45%'],
+        label: {
+          show: false,
+          position: 'outside'
+        },
+        emphasis: {
+          label: {
+            show: true,
+            fontSize: 15,
+            fontWeight: 'bold'
+          }
+        },
+        labelLine: {
+          show: true
+        },
+        data: typeData.value
+      }
+    ]})
+  chart.on('click', (params) => {
+    console.log('点击的数据值为:', params.value);
+    console.log('点击的数据类型为:', params.data.type);
+    debugger
+    const createTime = queryParams.createTime;
+    const deptId = queryParams.deptId;
+    const status = params.data.name
+    push({ name: 'IotInspectItemStat', params:{deptId,status,createTime}})
+  });
+  // echarts.init(statusChartRef.value).setOption({
+  // })
   //待执行
   initGaugeChart(
     writeTodayChartRef.value,