Sfoglia il codice sorgente

Merge branch 'master' into refactor/non-production-time

Zimo 1 giorno fa
parent
commit
8a83e667b5

+ 2 - 2
src/locales/zh-CN.ts

@@ -8,8 +8,8 @@ export default {
     maintained: '已保养',
     notInspected: '未巡检',
     inspected: '已巡检',
-    zj: '总进尺(m)',
-    xj: '总完成井数',
+    zj: '钻井总进尺(m)',
+    xj: '修井总完成井数',
     mttr: 'MTTR(平均解决时间)',
     materialsUnderInventory: '库存预警物料数量',
     deviceStatus: '设备状态统计',

+ 1 - 1
src/utils/useSocketBus.ts

@@ -4,7 +4,7 @@ import { useWebSocket } from '@vueuse/core'
 type EventHandler = (data: any) => void
 
 export function useSocketBus(deviceCode: string) {
-  const url = `ws://172.21.10.65:8080/ws/${deviceCode}`
+  const url = `${import.meta.env.VITE_BASE_URL}/ws/${deviceCode}`
   // const url = `ws://192.168.188.149:8080/ws/${deviceCode}`
 
   // 响应式状态

+ 2 - 89
src/views/oli-connection/monitoring/index.vue

@@ -292,93 +292,6 @@ const openDetail = (
               :class="width"
               view-class="grid grid-cols-4 grid-rows-3 gap-4 p-4"
             >
-              <!-- <div
-                v-for="item in list"
-                :key="item.id"
-                class="group relative flex flex-col bg-white dark:bg-[#262727] rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 hover:shadow-xl hover:-translate-y-1 transition-all duration-300 overflow-hidden"
-              >
-                <div
-                  class="h-[110px] px-4 pt-3 pb-1 flex flex-col justify-between"
-                  :class="getStatusConfig(item.ifInline).bg"
-                >
-                  <div class="flex justify-between items-center text-white">
-                    <div class="bg-white/20 p-1.5 rounded-full backdrop-blur-sm">
-                      <Icon icon="ep:cpu" class="text-lg" />
-                    </div>
-
-                    <div
-                      class="flex items-center gap-1 bg-black/20 backdrop-blur-md px-2 py-0.5 rounded-full text-xs font-medium"
-                    >
-                      <Icon :icon="getStatusConfig(item.ifInline).icon" />
-                      <span>{{ getStatusConfig(item.ifInline).label }}</span>
-                    </div>
-                  </div>
-
-                  <div class="text-white mb-1">
-                    <div class="text-lg font-bold truncate drop-shadow-md" :title="item.deviceName">
-                      {{ item.deviceName }}
-                    </div>
-                    <div class="text-xs opacity-80 font-mono truncate">
-                      {{ item.deviceCode }}
-                    </div>
-                  </div>
-                </div>
-
-                <div
-                  class="flex-1 p-4 flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-300"
-                >
-                  <div
-                    class="flex items-center justify-between border-b border-gray-50 dark:border-gray-700 pb-2"
-                  >
-                    <span class="text-gray-400 text-xs flex items-center gap-1">
-                      <Icon icon="ep:postcard" /> 编码
-                    </span>
-                    <span
-                      class="font-mono font-medium truncate max-w-[140px]"
-                      :title="item.deviceCode"
-                    >
-                      {{ item.deviceCode }}
-                    </span>
-                  </div>
-
-                  <div
-                    class="flex items-center justify-between border-b border-gray-50 dark:border-gray-700 pb-2"
-                  >
-                    <span class="text-gray-400 text-xs flex items-center gap-1">
-                      <Icon icon="ep:price-tag" /> 类别
-                    </span>
-                    <el-tag
-                      size="small"
-                      type="info"
-                      effect="plain"
-                      class="!border-none !bg-gray-100 dark:!bg-gray-800"
-                    >
-                      {{ item.assetClassName || '-' }}
-                    </el-tag>
-                  </div>
-
-                  <div class="flex items-center justify-between">
-                    <span class="text-gray-400 text-xs flex items-center gap-1">
-                      <Icon icon="ep:clock" /> 最后上线
-                    </span>
-                    <span class="text-xs">
-                      {{ item.lastInlineTime || '从未上线' }}
-                    </span>
-                  </div>
-                </div>
-
-                <div
-                  class="p-3 bg-gray-50 dark:bg-[#1d1e1f] border-t border-gray-100 dark:border-gray-700 flex justify-end"
-                >
-                  <el-button
-                    type="primary"
-                    link
-                    class="!px-2 group-hover:scale-105 transition-transform"
-                  >
-                    查看详情 <Icon icon="ep:arrow-right" class="ml-1" />
-                  </el-button>
-                </div>
-              </div> -->
               <div
                 v-for="item in list"
                 :key="item.id"
@@ -388,7 +301,7 @@ const openDetail = (
                   class="h-[80px] px-4 flex items-center justify-between overflow-hidden"
                   :class="getStatusConfig(item.ifInline).bg"
                 >
-                  <div class="flex items-center gap-3 z-10 max-w-[95%]">
+                  <div class="flex items-center gap-3 z-10 max-w-[80%]">
                     <div class="bg-white/20 p-2 rounded-lg backdrop-blur-md shadow-inner shrink-0">
                       <Icon :icon="item.carId ? 'ep:van' : 'ep:cpu'" class="text-xl text-white" />
                     </div>
@@ -397,7 +310,7 @@ const openDetail = (
                     <div class="flex flex-col overflow-hidden">
                       <el-tooltip effect="dark" :content="item.deviceName" placement="top-start">
                         <span
-                          class="text-white font-bold text-base truncate leading-tight"
+                          class="text-white font-bold text-base leading-tight"
                           :title="item.deviceName"
                         >
                           {{ item.deviceName }}

+ 171 - 72
src/views/pms/device/IotDeviceForm.vue

@@ -1,6 +1,12 @@
 <template>
   <ContentWrap v-loading="formLoading">
-    <el-form ref="formRef" :model="formData" :rules="formRules" style="margin-right: 4em;margin-left: 0.5em" label-width="130px">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      style="margin-right: 4em; margin-left: 0.5em"
+      label-width="130px"
+    >
       <div class="title-div">
         <el-button @click="baseInfoClick" class="title-button">
           <Icon color="black" icon="ep:set-up" :size="18" class="cursor-pointer first-icon" />
@@ -18,24 +24,33 @@
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.yfClass')" prop="yfClass">
               <el-cascader
-                :disabled="formType==='update'&&formData.yfDeviceCode"
+                :disabled="formType === 'update' && formData.yfDeviceCode"
                 style="width: 100%"
                 v-model="formData.yfClass"
                 :options="yfclasses"
                 :props="{ expandTrigger: 'hover' }"
                 clearable
                 filterable
-                @change="handleYfClassChange" />
+                @change="handleYfClassChange"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.yfCode')" prop="yfDeviceCode">
-              <el-input v-model="formData.yfDeviceCode" :disabled="formData.yfDeviceCode" placeholder="请输入油服设备编码" />
+              <el-input
+                v-model="formData.yfDeviceCode"
+                :disabled="formData.yfDeviceCode"
+                placeholder="请输入油服设备编码"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.code')" prop="deviceCode">
-              <el-input v-model="formData.deviceCode" :disabled="formType==='update'" placeholder="请输入设备编码" />
+              <el-input
+                v-model="formData.deviceCode"
+                :disabled="formType === 'update'"
+                placeholder="请输入设备编码"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="8">
@@ -46,7 +61,7 @@
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.dept')" prop="deptId">
               <el-tree-select
-                :disabled="formType==='update'"
+                :disabled="formType === 'update'"
                 v-model="formData.deptId"
                 :data="deptList"
                 :props="defaultProps"
@@ -55,20 +70,20 @@
                 filterable
                 placeholder="请选择所在部门"
               />
-<!--              <el-tree-select-->
-<!--                v-model="formData.deptId"-->
-<!--                :data="deptList"-->
-<!--                :props="defaultProps"-->
-<!--                check-strictly-->
-<!--                node-key="id"-->
-<!--                placeholder="请选择归属部门"-->
-<!--              />-->
+              <!--              <el-tree-select-->
+              <!--                v-model="formData.deptId"-->
+              <!--                :data="deptList"-->
+              <!--                :props="defaultProps"-->
+              <!--                check-strictly-->
+              <!--                node-key="id"-->
+              <!--                placeholder="请选择归属部门"-->
+              <!--              />-->
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('deviceForm.category')" prop="assetClass">
               <el-tree-select
-                :disabled="formType==='update'&&username!=='超级管理员'"
+                :disabled="formType === 'update' && username !== '超级管理员'"
                 v-model="formData.assetClass"
                 :data="productClassifyList"
                 :props="defaultProps"
@@ -82,7 +97,12 @@
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.status')" prop="deviceStatus">
-              <el-select v-model="formData.deviceStatus" :placeholder="t('deviceForm.choose')" :disabled="formType==='update'" clearable>
+              <el-select
+                v-model="formData.deviceStatus"
+                :placeholder="t('deviceForm.choose')"
+                :disabled="formType === 'update'"
+                clearable
+              >
                 <el-option
                   v-for="dict in getStrDictOptions(DICT_TYPE.PMS_DEVICE_STATUS)"
                   :key="dict.label"
@@ -115,8 +135,19 @@
               />
             </el-form-item>
           </el-col>
-          <el-col :span="8" >
-            <div style="display: flex;flex-direction: row">
+          <el-col :span="8">
+            <el-form-item label="车牌号" prop="carNo">
+              <el-input clearable v-model="formData.carNo" placeholder="请输入车牌号" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="设备号" prop="deviceNo">
+              <el-input clearable v-model="formData.deviceNo" placeholder="请输入设备号" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <div style="display: flex; flex-direction: row">
               <el-form-item :label="t('deviceForm.model')" prop="model" style="width: 86%">
                 <el-input
                   clearable
@@ -144,7 +175,11 @@
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('deviceForm.remark')" prop="remark">
-              <el-input v-model="formData.remark" type="textarea" :placeholder="t('deviceForm.remarkHolder')" />
+              <el-input
+                v-model="formData.remark"
+                type="textarea"
+                :placeholder="t('deviceForm.remarkHolder')"
+              />
             </el-form-item>
           </el-col>
         </el-row>
@@ -220,7 +255,11 @@
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('deviceForm.ni')" prop="nameplate">
-              <el-input v-model="formData.nameplate" type="textarea" :placeholder="t('deviceForm.niHolder')"/>
+              <el-input
+                v-model="formData.nameplate"
+                type="textarea"
+                :placeholder="t('deviceForm.niHolder')"
+              />
             </el-form-item>
           </el-col>
         </el-row>
@@ -240,56 +279,82 @@
       <div class="cw-expandable-content" :class="{ 'is-expanded': cwIsExpanded }">
         <el-row>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'采购价格':'租赁价格'" prop="plPrice">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '采购价格' : '租赁价格'"
+              prop="plPrice"
+            >
               <el-input
                 v-model="formData.plPrice"
                 @input="handleInput(formData.plPrice, 'plPrice')"
-                :placeholder="formData.assetProperty==='zy'?'请输入采购价格':'请输入租赁价格'"
+                :placeholder="formData.assetProperty === 'zy' ? '请输入采购价格' : '请输入租赁价格'"
               />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'采购日期':'租赁日期'" prop="plDate">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '采购日期' : '租赁日期'"
+              prop="plDate"
+            >
               <el-date-picker
                 style="width: 150%"
                 v-model="formData.plDate"
                 type="date"
                 value-format="x"
-                :placeholder="formData.assetProperty==='zy'?'请输入采购日期':'请输入租赁日期'"
+                :placeholder="formData.assetProperty === 'zy' ? '请输入采购日期' : '请输入租赁日期'"
               />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'折旧年限':'租赁年限'" prop="plYear">
-              <el-input v-model="formData.plYear" type="number" :placeholder="formData.assetProperty==='zy'?'请输入折旧年限':'请输入租赁年限'" />
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '折旧年限' : '租赁年限'"
+              prop="plYear"
+            >
+              <el-input
+                v-model="formData.plYear"
+                type="number"
+                :placeholder="formData.assetProperty === 'zy' ? '请输入折旧年限' : '请输入租赁年限'"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'折旧开始日期':'租赁开始日期'" prop="plStartDate">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '折旧开始日期' : '租赁开始日期'"
+              prop="plStartDate"
+            >
               <el-date-picker
                 style="width: 150%"
                 v-model="formData.plStartDate"
                 type="date"
                 value-format="x"
-                :placeholder="formData.assetProperty==='zy'?'请选择折旧开始日期':'请选择租赁开始日期'"
+                :placeholder="
+                  formData.assetProperty === 'zy' ? '请选择折旧开始日期' : '请选择租赁开始日期'
+                "
               />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'已提折旧月数':'已租赁月数'" prop="plMonthed">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '已提折旧月数' : '已租赁月数'"
+              prop="plMonthed"
+            >
               <el-input
                 v-model="formData.plMonthed"
                 type="number"
-                :placeholder="formData.assetProperty==='zy'?'请输入已提折旧月数':'请输入已租赁月数'"
+                :placeholder="
+                  formData.assetProperty === 'zy' ? '请输入已提折旧月数' : '请输入已租赁月数'
+                "
               />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'已提折旧金额':'已租赁金额'" prop="plAmounted">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '已提折旧金额' : '已租赁金额'"
+              prop="plAmounted"
+            >
               <el-input
                 v-model="formData.plAmounted"
                 @input="handleInput(formData.plAmounted, 'plAmounted')"
-                :placeholder="formData.assetProperty==='zy'?'请输入已提折旧金额':'已租赁金额'"
+                :placeholder="formData.assetProperty === 'zy' ? '请输入已提折旧金额' : '已租赁金额'"
               />
             </el-form-item>
           </el-col>
@@ -319,7 +384,12 @@
       <div class="qt-expandable-content" :class="{ 'is-expanded': qtIsExpanded }">
         <el-row>
           <el-col v-for="field in list" :key="field.sort" :span="8">
-            <el-form-item label-width="180px" :label="field.name" :prop="field.code" :rules="field.rules">
+            <el-form-item
+              label-width="180px"
+              :label="field.name"
+              :prop="field.code"
+              :rules="field.rules"
+            >
               <!-- 文本输入 -->
               <el-input
                 v-if="field.type === 'text'"
@@ -331,7 +401,7 @@
               <el-select
                 v-else-if="field.type === 'enum'"
                 v-model="formData[field.code]"
-                :placeholder="'请输入'+field.name"
+                :placeholder="'请输入' + field.name"
                 clearable
                 filterable
               >
@@ -382,7 +452,7 @@
     </el-form>
   </ContentWrap>
   <BrandList ref="brandFormRef" @choose="brandChoose" />
-  <ModelList ref="modelFormRef" @choose="modelChoose" :brand = "formData.brand" />
+  <ModelList ref="modelFormRef" @choose="modelChoose" :brand="formData.brand" />
   <CustomerList ref="customerZzFormRef" @choose="customerZzChoose" />
   <CustomerList ref="customerSupplierFormRef" @choose="customerSupplierChoose" />
 </template>
@@ -396,10 +466,11 @@ import * as DeptApi from '@/api/system/dept'
 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 { DeviceAttrModelApi } from '@/api/pms/deviceattrmodel'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
-import {IotYfClassifyApi} from "@/api/pms/yfclass";
-import { useRefreshStore } from '@/store/modules/pms/refreshStore';
+import { IotYfClassifyApi } from '@/api/pms/yfclass'
+import { useRefreshStore } from '@/store/modules/pms/refreshStore'
+import { watch } from 'vue'
 
 /** 设备台账 表单 */
 defineOptions({ name: 'DeviceDetailAdd' })
@@ -412,7 +483,7 @@ const username = ref('')
 const deptList = ref<Tree[]>([]) // 树形结构
 const productClassifyList = ref<Tree[]>([]) // 树形结构
 const { delView } = useTagsViewStore() // 视图操作
-const { params, name,query } = useRoute() // 查询参数
+const { params, name, query } = useRoute() // 查询参数
 const { currentRoute, push } = useRouter()
 const { wsCache } = useCache()
 const id = params.id
@@ -429,7 +500,7 @@ const brandLabel = ref('') // 表单的类型:create - 新增;update - 修
 const zzLabel = ref('') // 表单的类型:create - 新增;update - 修改
 const supplierLabel = ref('') // 表单的类型:create - 新增;update - 修改
 const yfclasses = ref([])
-const refreshStore = useRefreshStore();
+const refreshStore = useRefreshStore()
 
 const formData = ref({
   id: undefined,
@@ -468,10 +539,24 @@ const formData = ref({
   infoRemark: undefined,
   infoUrl: undefined,
   templateJson: undefined,
-  assetClass: undefined
+  assetClass: undefined,
+  carNo: undefined,
+  deviceNo: undefined
 })
 const formRules = reactive({
-  yfClass: [{ required: true, message: '编码类别不能为空', trigger: 'blur' }],
+  yfClass: [
+    {
+      validator: (rule, value, callback) => {
+        // 当资产性质为租赁('zl')时,yfClass非必填;否则必填
+        if (formData.value.assetProperty === 'zl' || value) {
+          callback() // 租赁资产或有值时通过验证
+        } else {
+          callback(new Error('编码类别不能为空')) // 非租赁资产且无值时失败
+        }
+      },
+      trigger: 'blur'
+    }
+  ],
   yfDeviceCode: [{ required: true, message: '油服编码不能为空', trigger: 'blur' }],
   assetClass: [{ required: true, message: '资产类别不能为空', trigger: 'blur' }],
   deviceCode: [{ required: true, message: '设备编码不能为空', trigger: 'blur' }],
@@ -485,19 +570,19 @@ const formRules = reactive({
 })
 
 const list = ref([])
-const handleYfClassChange = async (value) =>{
+const handleYfClassChange = async (value) => {
   console.log(value)
   const prefix = value.join('')
   const last = await IotDeviceApi.getMaxCode(prefix)
-  formData.value.yfDeviceCode = prefix+last
+  formData.value.yfDeviceCode = prefix + last
 }
 const assetclasschange = () => {
   const assetClass = formData.value.assetClass
-  DeviceAttrModelApi.getDeviceAttrModelListByDeviceCategoryId(assetClass).then(res => {
-    if (res){
+  DeviceAttrModelApi.getDeviceAttrModelListByDeviceCategoryId(assetClass).then((res) => {
+    if (res) {
       res.forEach((item) => {
         if (item.requiredFlag) {
-          const rule = {required: true, message: item.name+'不能为空', trigger: 'blur'}
+          const rule = { required: true, message: item.name + '不能为空', trigger: 'blur' }
           item.rules = []
           item.rules.push(rule)
         }
@@ -509,12 +594,25 @@ const assetclasschange = () => {
   })
 }
 
+watch(
+  () => formData.value.assetProperty,
+  (newVal) => {
+    nextTick(() => {
+      if (formRef.value) {
+        // 重新验证 yfClass 和 yfCode 字段
+        formRef.value.validateField('yfClass')
+      }
+    })
+  },
+  { immediate: true }
+)
+
 const brandChoose = (row) => {
   formData.value.brand = row.id
   // brandLabel.value = row.value
   formData.value.brandName = row.label
 }
-const brandClear = () =>{
+const brandClear = () => {
   formData.value.brand = undefined
   formData.value.brandName = undefined
 }
@@ -535,7 +633,7 @@ const customerZzChoose = (row) => {
   // zzLabel.value = row.name
   formData.value.manufacturerName = row.name
 }
-const zzClear = () =>{
+const zzClear = () => {
   formData.value.manufacturerId = undefined
   formData.value.manufacturerName = undefined
 }
@@ -545,7 +643,7 @@ const openForm = () => {
   brandFormRef.value.open()
 }
 const modelFormRef = ref()
-const openModelForm = () =>{
+const openModelForm = () => {
   modelFormRef.value.open()
 }
 const customerSupplierFormRef = ref()
@@ -594,7 +692,7 @@ const handleInput = (value, obj) => {
 
 const close = () => {
   delView(unref(currentRoute))
-  push({ name: 'IotDevicePms', params:{}})
+  push({ name: 'IotDevicePms', params: {} })
   // delView(unref(currentRoute))
   // push({
   //   name: 'IotDevicePms',
@@ -625,13 +723,13 @@ const submitForm = async () => {
   formLoading.value = true
   try {
     if (list.value) {
-      list.value = list.value.map(item => ({
+      list.value = list.value.map((item) => ({
         ...item,
         value: formData.value[item.code] // 自定义属性生成逻辑
       }))
       formData.value.templateJson = JSON.stringify(list.value)
     }
-    formData.value.yfClass = formData.value.yfClass.join(',');
+    formData.value.yfClass = formData.value.yfClass.join(',')
     const data = formData.value as unknown as IotDeviceVO
     if (formType.value === 'create') {
       await IotDeviceApi.createIotDevice(data)
@@ -643,11 +741,11 @@ const submitForm = async () => {
     dialogVisible.value = false
     // 发送操作成功的事件
     //emit('success')
-    const sourcePage = query.source as string;
+    const sourcePage = query.source as string
 
     // 如果有来源页面标识,触发原页面的刷新
     if (sourcePage) {
-      refreshStore.triggerRefresh(sourcePage);
+      refreshStore.triggerRefresh(sourcePage)
     }
     close()
   } finally {
@@ -666,20 +764,22 @@ onMounted(async () => {
   formData.value.assetProperty = 'zy'
   // 修改时,设置数据
   if (id) {
-    formType.value = 'update';
+    formType.value = 'update'
     formLoading.value = true
     try {
-      const iotDevice = await IotDeviceApi.getIotDevice(id);
+      const iotDevice = await IotDeviceApi.getIotDevice(id)
       formData.value = iotDevice
-      formData.value.brandName = iotDevice.brandName;
-      formData.value.manufacturerName = iotDevice.zzName;
-      formData.value.supplierName = iotDevice.supplierName;
+      formData.value.brandName = iotDevice.brandName
+      formData.value.manufacturerName = iotDevice.zzName
+      formData.value.supplierName = iotDevice.supplierName
+      formData.value.carNo = iotDevice.carNo
+      formData.value.deviceNo = iotDevice.deviceNo
       if (iotDevice.yfClass) {
-        formData.value.yfClass = iotDevice.yfClass.split(',');
+        formData.value.yfClass = iotDevice.yfClass.split(',')
       }
-      list.value = JSON.parse(iotDevice.templateJson);
+      list.value = JSON.parse(iotDevice.templateJson)
       list.value.forEach((item) => {
-        formData.value[item.code] = item.value;
+        formData.value[item.code] = item.value
       })
     } finally {
       formLoading.value = false
@@ -688,11 +788,11 @@ onMounted(async () => {
     if (deptId) {
       formData.value.deptId = Number(deptId)
     }
-    formType.value = 'create';
+    formType.value = 'create'
   }
-  await IotYfClassifyApi.getChildrenList().then(res => {
+  await IotYfClassifyApi.getChildrenList().then((res) => {
     yfclasses.value = res
-  });
+  })
 })
 /** 重置表单 */
 const resetForm = () => {
@@ -725,8 +825,7 @@ const resetForm = () => {
     infoRemark: undefined,
     infoUrl: undefined,
     templateJson: undefined,
-    assetClass: undefined,
-
+    assetClass: undefined
   }
   formRef.value?.resetFields()
 }
@@ -769,18 +868,18 @@ const resetForm = () => {
 .qt-expandable-content.is-expanded {
   max-height: 1200px; /* 或者根据内容设定一个合适的最大高度 */
 }
-.title-button{
+.title-button {
   font-size: 18px;
   border: none;
 }
-.title-div{
+.title-div {
   margin-bottom: 20px;
   margin-top: 10px;
 }
-.cursor-pointer{
+.cursor-pointer {
   vertical-align: middle;
 }
-.first-icon{
+.first-icon {
   margin-bottom: 2px;
 }
 </style>

+ 184 - 77
src/views/pms/device/IotDeviceFormAdd.vue

@@ -1,6 +1,12 @@
 <template>
   <ContentWrap v-loading="formLoading">
-    <el-form ref="formRef" :model="formData" :rules="formRules" style="margin-right: 4em;margin-left: 0.5em" label-width="130px">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      style="margin-right: 4em; margin-left: 0.5em"
+      label-width="130px"
+    >
       <div class="title-div">
         <el-button @click="baseInfoClick" class="title-button">
           <Icon color="black" icon="ep:set-up" :size="18" class="cursor-pointer first-icon" />
@@ -16,38 +22,51 @@
       <div class="base-expandable-content" :class="{ 'is-expanded': baseIsExpanded }">
         <el-row>
           <el-col :span="8">
-            <el-form-item :label="t('iotDevice.yfClass')" prop="yfClass">
+            <el-form-item
+              :label="t('iotDevice.yfClass')"
+              prop="yfClass"
+              :required="formData.assetProperty !== 'zl'"
+            >
               <el-cascader
-                :disabled="formType==='update'&&formData.yfDeviceCode"
+                :disabled="formType === 'update' && formData.yfDeviceCode"
                 style="width: 100%"
                 v-model="formData.yfClass"
                 :options="yfclasses"
                 :props="{ expandTrigger: 'hover' }"
                 clearable
                 filterable
-                @change="handleYfClassChange" />
+                @change="handleYfClassChange"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.yfCode')" prop="yfDeviceCode">
-              <el-input v-model="formData.yfDeviceCode" :disabled="formData.yfDeviceCode" placeholder="请输入油服设备编码" />
+              <el-input
+                v-model="formData.yfDeviceCode"
+                :disabled="formData.yfDeviceCode"
+                placeholder="请输入油服设备编码"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.code')" prop="deviceCode">
-              <el-input v-model="formData.deviceCode" :disabled="formType==='update'" placeholder="请输入设备编码" />
+              <el-input
+                v-model="formData.deviceCode"
+                :disabled="formType === 'update'"
+                placeholder="请输入设备编码"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.name')" prop="deviceName">
-<!--              <el-input v-model="formData.deviceName" placeholder="请输入设备名称" />-->
+              <!--              <el-input v-model="formData.deviceName" placeholder="请输入设备名称" />-->
               <lang-input v-model="formData.deviceName" placeholder="请输入设备名称" />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.dept')" prop="deptId">
               <el-tree-select
-                :disabled="formType==='update'"
+                :disabled="formType === 'update'"
                 v-model="formData.deptId"
                 :data="deptList"
                 :props="defaultProps"
@@ -56,20 +75,20 @@
                 filterable
                 placeholder="请选择所在部门"
               />
-<!--              <el-tree-select-->
-<!--                v-model="formData.deptId"-->
-<!--                :data="deptList"-->
-<!--                :props="defaultProps"-->
-<!--                check-strictly-->
-<!--                node-key="id"-->
-<!--                placeholder="请选择归属部门"-->
-<!--              />-->
+              <!--              <el-tree-select-->
+              <!--                v-model="formData.deptId"-->
+              <!--                :data="deptList"-->
+              <!--                :props="defaultProps"-->
+              <!--                check-strictly-->
+              <!--                node-key="id"-->
+              <!--                placeholder="请选择归属部门"-->
+              <!--              />-->
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('deviceForm.category')" prop="assetClass">
               <el-tree-select
-                :disabled="formType==='update'"
+                :disabled="formType === 'update'"
                 v-model="formData.assetClass"
                 :data="productClassifyList"
                 :props="defaultProps"
@@ -83,7 +102,12 @@
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('iotDevice.status')" prop="deviceStatus">
-              <el-select v-model="formData.deviceStatus" :placeholder="t('deviceForm.choose')" :disabled="formType==='update'" clearable>
+              <el-select
+                v-model="formData.deviceStatus"
+                :placeholder="t('deviceForm.choose')"
+                :disabled="formType === 'update'"
+                clearable
+              >
                 <el-option
                   v-for="dict in getStrDictOptions(DICT_TYPE.PMS_DEVICE_STATUS)"
                   :key="dict.label"
@@ -116,8 +140,20 @@
               />
             </el-form-item>
           </el-col>
-          <el-col :span="8" >
-            <div style="display: flex;flex-direction: row">
+
+          <el-col :span="8">
+            <el-form-item label="车牌号" prop="carNo">
+              <el-input clearable v-model="formData.carNo" placeholder="请输入车牌号" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="设备号" prop="deviceNo">
+              <el-input clearable v-model="formData.deviceNo" placeholder="请输入设备号" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <div style="display: flex; flex-direction: row">
               <el-form-item :label="t('deviceForm.model')" prop="model" style="width: 86%">
                 <el-input
                   clearable
@@ -143,13 +179,18 @@
               <UploadImg v-model="formData.picUrl" :disabled="isDetail" height="60px" />
             </el-form-item>
           </el-col>
-          <el-col :span="16">
+          <el-col :span="8">
             <el-form-item :label="t('deviceForm.remark')" prop="remark">
-              <el-input v-model="formData.remark" type="textarea" :placeholder="t('deviceForm.remarkHolder')" />
+              <el-input
+                v-model="formData.remark"
+                type="textarea"
+                :placeholder="t('deviceForm.remarkHolder')"
+              />
             </el-form-item>
           </el-col>
         </el-row>
       </div>
+
       <div class="title-div">
         <el-button @click="zzInfoClick" class="title-button">
           <Icon color="black" icon="ep:set-up" :size="18" class="cursor-pointer first-icon" />
@@ -221,7 +262,11 @@
           </el-col>
           <el-col :span="8">
             <el-form-item :label="t('deviceForm.ni')" prop="nameplate">
-              <el-input v-model="formData.nameplate" type="textarea" :placeholder="t('deviceForm.niHolder')"/>
+              <el-input
+                v-model="formData.nameplate"
+                type="textarea"
+                :placeholder="t('deviceForm.niHolder')"
+              />
             </el-form-item>
           </el-col>
         </el-row>
@@ -241,56 +286,82 @@
       <div class="cw-expandable-content" :class="{ 'is-expanded': cwIsExpanded }">
         <el-row>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'采购价格':'租赁价格'" prop="plPrice">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '采购价格' : '租赁价格'"
+              prop="plPrice"
+            >
               <el-input
                 v-model="formData.plPrice"
                 @input="handleInput(formData.plPrice, 'plPrice')"
-                :placeholder="formData.assetProperty==='zy'?'请输入采购价格':'请输入租赁价格'"
+                :placeholder="formData.assetProperty === 'zy' ? '请输入采购价格' : '请输入租赁价格'"
               />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'采购日期':'租赁日期'" prop="plDate">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '采购日期' : '租赁日期'"
+              prop="plDate"
+            >
               <el-date-picker
                 style="width: 150%"
                 v-model="formData.plDate"
                 type="date"
                 value-format="x"
-                :placeholder="formData.assetProperty==='zy'?'请输入采购日期':'请输入租赁日期'"
+                :placeholder="formData.assetProperty === 'zy' ? '请输入采购日期' : '请输入租赁日期'"
               />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'折旧年限':'租赁年限'" prop="plYear">
-              <el-input v-model="formData.plYear" type="number" :placeholder="formData.assetProperty==='zy'?'请输入折旧年限':'请输入租赁年限'" />
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '折旧年限' : '租赁年限'"
+              prop="plYear"
+            >
+              <el-input
+                v-model="formData.plYear"
+                type="number"
+                :placeholder="formData.assetProperty === 'zy' ? '请输入折旧年限' : '请输入租赁年限'"
+              />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'折旧开始日期':'租赁开始日期'" prop="plStartDate">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '折旧开始日期' : '租赁开始日期'"
+              prop="plStartDate"
+            >
               <el-date-picker
                 style="width: 150%"
                 v-model="formData.plStartDate"
                 type="date"
                 value-format="x"
-                :placeholder="formData.assetProperty==='zy'?'请选择折旧开始日期':'请选择租赁开始日期'"
+                :placeholder="
+                  formData.assetProperty === 'zy' ? '请选择折旧开始日期' : '请选择租赁开始日期'
+                "
               />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'已提折旧月数':'已租赁月数'" prop="plMonthed">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '已提折旧月数' : '已租赁月数'"
+              prop="plMonthed"
+            >
               <el-input
                 v-model="formData.plMonthed"
                 type="number"
-                :placeholder="formData.assetProperty==='zy'?'请输入已提折旧月数':'请输入已租赁月数'"
+                :placeholder="
+                  formData.assetProperty === 'zy' ? '请输入已提折旧月数' : '请输入已租赁月数'
+                "
               />
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item :label="formData.assetProperty==='zy'?'已提折旧金额':'已租赁金额'" prop="plAmounted">
+            <el-form-item
+              :label="formData.assetProperty === 'zy' ? '已提折旧金额' : '已租赁金额'"
+              prop="plAmounted"
+            >
               <el-input
                 v-model="formData.plAmounted"
                 @input="handleInput(formData.plAmounted, 'plAmounted')"
-                :placeholder="formData.assetProperty==='zy'?'请输入已提折旧金额':'已租赁金额'"
+                :placeholder="formData.assetProperty === 'zy' ? '请输入已提折旧金额' : '已租赁金额'"
               />
             </el-form-item>
           </el-col>
@@ -320,7 +391,12 @@
       <div class="qt-expandable-content" :class="{ 'is-expanded': qtIsExpanded }">
         <el-row>
           <el-col v-for="field in list" :key="field.sort" :span="8">
-            <el-form-item label-width="180px" :label="field.name" :prop="field.code" :rules="field.rules">
+            <el-form-item
+              label-width="180px"
+              :label="field.name"
+              :prop="field.code"
+              :rules="field.rules"
+            >
               <!-- 文本输入 -->
               <el-input
                 v-if="field.type === 'text'"
@@ -332,7 +408,7 @@
               <el-select
                 v-else-if="field.type === 'enum'"
                 v-model="formData[field.code]"
-                :placeholder="'请输入'+field.name"
+                :placeholder="'请输入' + field.name"
                 clearable
                 filterable
               >
@@ -383,7 +459,7 @@
     </el-form>
   </ContentWrap>
   <BrandList ref="brandFormRef" @choose="brandChoose" />
-  <ModelList ref="modelFormRef" @choose="modelChoose" :brand = "formData.brand" />
+  <ModelList ref="modelFormRef" @choose="modelChoose" :brand="formData.brand" />
   <CustomerList ref="customerZzFormRef" @choose="customerZzChoose" />
   <CustomerList ref="customerSupplierFormRef" @choose="customerSupplierChoose" />
 </template>
@@ -397,9 +473,10 @@ import * as DeptApi from '@/api/system/dept'
 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 {IotYfClassifyApi} from "@/api/pms/yfclass";
-import { useRefreshStore } from '@/store/modules/pms/refreshStore';
+import { DeviceAttrModelApi } from '@/api/pms/deviceattrmodel'
+import { IotYfClassifyApi } from '@/api/pms/yfclass'
+import { useRefreshStore } from '@/store/modules/pms/refreshStore'
+import { watch, nextTick } from 'vue'
 
 /** 设备台账 表单 */
 defineOptions({ name: 'DeviceDetailAddd' })
@@ -428,7 +505,7 @@ const brandLabel = ref('') // 表单的类型:create - 新增;update - 修
 const zzLabel = ref('') // 表单的类型:create - 新增;update - 修改
 const supplierLabel = ref('') // 表单的类型:create - 新增;update - 修改
 const yfclasses = ref([])
-const refreshStore = useRefreshStore();
+const refreshStore = useRefreshStore()
 const formData = ref({
   id: undefined,
   deviceCode: undefined,
@@ -463,10 +540,24 @@ const formData = ref({
   infoRemark: undefined,
   infoUrl: undefined,
   templateJson: undefined,
-  assetClass: undefined
+  assetClass: undefined,
+  carNo: undefined,
+  deviceNo: undefined
 })
 const formRules = reactive({
-  yfClass: [{ required: true, message: '编码类别不能为空', trigger: 'blur' }],
+  yfClass: [
+    {
+      validator: (rule, value, callback) => {
+        // 当资产性质为租赁('zl')时,yfClass非必填;否则必填
+        if (formData.value.assetProperty === 'zl' || value) {
+          callback() // 租赁资产或有值时通过验证
+        } else {
+          callback(new Error('编码类别不能为空')) // 非租赁资产且无值时失败
+        }
+      },
+      trigger: 'blur'
+    }
+  ],
   yfCode: [{ required: true, message: '油服编码不能为空', trigger: 'blur' }],
   assetClass: [{ required: true, message: '资产类别不能为空', trigger: 'blur' }],
   deviceCode: [{ required: true, message: '设备编码不能为空', trigger: 'blur' }],
@@ -476,18 +567,33 @@ const formRules = reactive({
   deviceStatus: [{ required: true, message: '设备状态不能为空', trigger: 'blur' }],
   assetProperty: [{ required: true, message: '资产性质不能为空', trigger: 'blur' }],
   manufacturerId: [{ required: true, message: '制造商id不能为空', trigger: 'blur' }],
-  manDate: [{ required: true, message: '生产日期不能为空', trigger: 'blur' }]
+  manDate: [{ required: true, message: '生产日期不能为空', trigger: 'blur' }],
+  carNo: [{ required: true, message: '车牌号不能为空', trigger: 'blur' }],
+  deviceNo: [{ required: true, message: '设备号不能为空', trigger: 'blur' }]
 })
 
+watch(
+  () => formData.value.assetProperty,
+  (newVal) => {
+    nextTick(() => {
+      if (formRef.value) {
+        // 重新验证 yfClass 和 yfCode 字段
+        formRef.value.validateField('yfClass')
+      }
+    })
+  },
+  { immediate: true }
+)
+
 const list = ref([])
 
 const assetclasschange = () => {
   const assetClass = formData.value.assetClass
-  DeviceAttrModelApi.getDeviceAttrModelListByDeviceCategoryId(assetClass).then(res => {
-    if (res){
+  DeviceAttrModelApi.getDeviceAttrModelListByDeviceCategoryId(assetClass).then((res) => {
+    if (res) {
       res.forEach((item) => {
         if (item.requiredFlag) {
-          const rule = {required: true, message: item.name+'不能为空', trigger: 'blur'}
+          const rule = { required: true, message: item.name + '不能为空', trigger: 'blur' }
           item.rules = []
           item.rules.push(rule)
         }
@@ -498,18 +604,18 @@ const assetclasschange = () => {
     }
   })
 }
-const handleYfClassChange = async (value) =>{
+const handleYfClassChange = async (value) => {
   console.log(value)
   const prefix = value.join('')
   const last = await IotDeviceApi.getMaxCode(prefix)
-  formData.value.yfDeviceCode = prefix+last
+  formData.value.yfDeviceCode = prefix + last
 }
 const brandChoose = (row) => {
   formData.value.brand = row.id
   // brandLabel.value = row.value
   formData.value.brandName = row.label
 }
-const brandClear = () =>{
+const brandClear = () => {
   formData.value.brand = undefined
   formData.value.brandName = undefined
 }
@@ -530,7 +636,7 @@ const customerZzChoose = (row) => {
   // zzLabel.value = row.name
   formData.value.manufacturerName = row.name
 }
-const zzClear = () =>{
+const zzClear = () => {
   formData.value.manufacturerId = undefined
   formData.value.manufacturerName = undefined
 }
@@ -540,7 +646,7 @@ const openForm = () => {
   brandFormRef.value.open()
 }
 const modelFormRef = ref()
-const openModelForm = () =>{
+const openModelForm = () => {
   modelFormRef.value.open()
 }
 const customerSupplierFormRef = ref()
@@ -589,7 +695,7 @@ const handleInput = (value, obj) => {
 
 const close = () => {
   delView(unref(currentRoute))
-  push({ name: 'IotDevicePms', params:{}})
+  push({ name: 'IotDevicePms', params: {} })
   // delView(unref(currentRoute))
   // push({
   //   name: 'IotDevicePms',
@@ -620,13 +726,13 @@ const submitForm = async () => {
   formLoading.value = true
   try {
     if (list.value) {
-      list.value = list.value.map(item => ({
+      list.value = list.value.map((item) => ({
         ...item,
         value: formData.value[item.code] // 自定义属性生成逻辑
       }))
       formData.value.templateJson = JSON.stringify(list.value)
     }
-    formData.value.yfClass = formData.value.yfClass.join(',');
+    formData.value.yfClass = formData.value.yfClass ? formData.value.yfClass.join(',') : ''
     const data = formData.value as unknown as IotDeviceVO
     if (formType.value === 'create') {
       await IotDeviceApi.createIotDevice(data)
@@ -638,11 +744,11 @@ const submitForm = async () => {
     dialogVisible.value = false
     // 发送操作成功的事件
     //emit('success')
-    const sourcePage = query.source as string;
+    const sourcePage = query.source as string
 
     // 如果有来源页面标识,触发原页面的刷新
     if (sourcePage) {
-      refreshStore.triggerRefresh(sourcePage);
+      refreshStore.triggerRefresh(sourcePage)
     }
     close()
   } finally {
@@ -658,20 +764,22 @@ onMounted(async () => {
   formData.value.assetProperty = 'zy'
   // 修改时,设置数据
   if (id) {
-    formType.value = 'update';
+    formType.value = 'update'
     formLoading.value = true
     try {
-      const iotDevice = await IotDeviceApi.getIotDevice(id);
+      const iotDevice = await IotDeviceApi.getIotDevice(id)
       formData.value = iotDevice
-      formData.value.brandName = iotDevice.brandName;
-      formData.value.manufacturerName = iotDevice.zzName;
-      formData.value.supplierName = iotDevice.supplierName;
-      list.value = JSON.parse(iotDevice.templateJson);
+      formData.value.brandName = iotDevice.brandName
+      formData.value.manufacturerName = iotDevice.zzName
+      formData.value.supplierName = iotDevice.supplierName
+      formData.value.carNo = iotDevice.carNo
+      formData.value.deviceNo = iotDevice.deviceNo
+      list.value = JSON.parse(iotDevice.templateJson)
       if (iotDevice.yfClass) {
-        formData.value.yfClass = iotDevice.yfClass.split(',');
+        formData.value.yfClass = iotDevice.yfClass.split(',')
       }
       list.value.forEach((item) => {
-        formData.value[item.code] = item.value;
+        formData.value[item.code] = item.value
       })
     } finally {
       formLoading.value = false
@@ -680,11 +788,11 @@ onMounted(async () => {
     if (deptId) {
       formData.value.deptId = Number(deptId)
     }
-    formType.value = 'create';
+    formType.value = 'create'
   }
-  await IotYfClassifyApi.getChildrenList().then(res => {
+  await IotYfClassifyApi.getChildrenList().then((res) => {
     yfclasses.value = res
-  });
+  })
 })
 /** 重置表单 */
 const resetForm = () => {
@@ -717,8 +825,7 @@ const resetForm = () => {
     infoRemark: undefined,
     infoUrl: undefined,
     templateJson: undefined,
-    assetClass: undefined,
-
+    assetClass: undefined
   }
   formRef.value?.resetFields()
 }
@@ -726,13 +833,13 @@ const resetForm = () => {
 
 <style scoped lang="scss">
 .base-expandable-content {
-  max-height: 0; /* 初始高度为0 */
+  max-height: 0px; /* 初始高度为0 */
   overflow: hidden; /* 隐藏溢出的内容 */
   transition: max-height 0.3s ease; /* 平滑过渡效果 */
 }
 
 .base-expandable-content.is-expanded {
-  min-height: 260px; /* 或者根据内容设定一个合适的最大高度 */
+  min-height: 360px; /* 或者根据内容设定一个合适的最大高度 */
 }
 .zz-expandable-content {
   max-height: 0; /* 初始高度为0 */
@@ -761,18 +868,18 @@ const resetForm = () => {
 .qt-expandable-content.is-expanded {
   max-height: 1200px; /* 或者根据内容设定一个合适的最大高度 */
 }
-.title-button{
+.title-button {
   font-size: 18px;
   border: none;
 }
-.title-div{
+.title-div {
   margin-bottom: 20px;
   margin-top: 10px;
 }
-.cursor-pointer{
+.cursor-pointer {
   vertical-align: middle;
 }
-.first-icon{
+.first-icon {
   margin-bottom: 2px;
 }
 </style>

+ 165 - 6
src/views/pms/device/index.vue

@@ -48,6 +48,25 @@
               class="!w-200px"
             />
           </el-form-item>
+          <el-form-item label="车牌号" prop="carNo">
+            <el-input
+              v-model="queryParams.carNo"
+              placeholder="请输入车牌号"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-200px"
+            />
+          </el-form-item>
+
+          <el-form-item label="设备号" prop="deviceNo">
+            <el-input
+              v-model="queryParams.deviceNo"
+              placeholder="请输入设备号"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-200px"
+            />
+          </el-form-item>
           <el-form-item :label="t('iotDevice.brand')" prop="brandName">
             <el-input
               v-model="queryParams.brandName"
@@ -155,7 +174,7 @@
           height="65vh"
           @sort-change="handleSortChange"
         >
-          <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
+          <el-table-column :label="t('iotDevice.serial')" width="70" align="center" fixed="left">
             <template #default="scope">
               {{ scope.$index + 1 }}
             </template>
@@ -166,27 +185,106 @@
             align="center"
             prop="yfDeviceCode"
             width="150"
-          />
+            fixed="left"
+          >
+            <template #header>
+              <span
+                style="display: inline-block"
+                class="text-[#ad9399] w-[70px] text-[12px] cursor-pointer z-[999] justify-center flex items-center"
+              >
+                <el-popover placement="bottom" :width="250" trigger="hover">
+                  <template #reference>
+                    <div class="flex items-center">
+                      <span> 油服编码 </span> <Icon icon="ep:arrow-down" />
+                    </div>
+                  </template>
+                  <div class="flex items-center gap-2">
+                    <el-input
+                      v-model="queryParams.yfDeviceCode"
+                      placeholder="请输入油服编码"
+                      style="width: 180px"
+                      clearable
+                      @keyup.enter="handleQuery"
+                    />
+                    <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
+                  </div>
+                </el-popover>
+              </span>
+            </template>
+          </el-table-column>
           <el-table-column
             :label="t('iotDevice.code')"
             sortable
             align="center"
             prop="deviceCode"
             width="150"
-          />
+            fixed="left"
+          >
+            <template #header>
+              <span
+                style="display: inline-block"
+                class="text-[#ad9399] w-[70px] text-[12px] cursor-pointer z-[999] justify-center flex items-center"
+              >
+                <el-popover placement="bottom" :width="250" trigger="hover">
+                  <template #reference>
+                    <div class="flex items-center">
+                      <span> 历史编码 </span> <Icon icon="ep:arrow-down" />
+                    </div>
+                  </template>
+                  <div class="flex items-center gap-2">
+                    <el-input
+                      v-model="queryParams.deviceCode"
+                      placeholder="请输入历史编码"
+                      style="width: 180px"
+                      clearable
+                      @keyup.enter="handleQuery"
+                    />
+                    <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
+                  </div>
+                </el-popover>
+              </span>
+            </template>
+          </el-table-column>
+
           <el-table-column
             :label="t('iotDevice.name')"
             sortable
             align="center"
             prop="deviceName"
-            min-width="250"
+            min-width="280"
           >
+            <template #header>
+              <span
+                style="display: inline-block"
+                class="text-[#ad9399] w-[70px] text-[12px] cursor-pointer z-[999] justify-center flex items-center"
+              >
+                <el-popover placement="bottom" :width="250" trigger="hover">
+                  <template #reference>
+                    <div class="flex items-center">
+                      <span> 设备名称 </span> <Icon icon="ep:arrow-down" />
+                    </div>
+                  </template>
+                  <div class="flex items-center gap-2">
+                    <el-input
+                      v-model="queryParams.deviceName"
+                      placeholder="请输入设备名称"
+                      style="width: 180px"
+                      clearable
+                      @keyup.enter="handleQuery"
+                    />
+                    <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
+                  </div>
+                </el-popover>
+              </span>
+            </template>
+
             <template #default="scope">
               <el-link :underline="false" type="primary" @click="handleDetail(scope.row.id)">
                 {{ scope.row.deviceName }}
               </el-link>
             </template>
           </el-table-column>
+          <el-table-column label="设备号" sortable align="center" prop="deviceNo" width="120" />
           <el-table-column
             :label="t('iotDevice.dept')"
             align="center"
@@ -197,8 +295,27 @@
             :label="t('iotDevice.status')"
             align="center"
             prop="deviceStatus"
-            min-width="90"
+            min-width="150"
           >
+            <template #header>
+              <div class="flex items-center justify-center pb-[1px]">
+                <el-dropdown @command="handleCommand">
+                  <span class="text-[#ad9399] text-[12px] cursor-pointer flex items-center">
+                    <span> 设备状态 </span> <Icon icon="ep:arrow-down" />
+                  </span>
+                  <template #dropdown>
+                    <el-dropdown-menu>
+                      <el-dropdown-item
+                        v-for="item in getStrDictOptions(DICT_TYPE.PMS_DEVICE_STATUS)"
+                        :key="item.label"
+                        :command="item.value"
+                        >{{ item.label }}</el-dropdown-item
+                      >
+                    </el-dropdown-menu>
+                  </template>
+                </el-dropdown>
+              </div>
+            </template>
             <template #default="scope">
               <dict-tag :type="DICT_TYPE.PMS_DEVICE_STATUS" :value="scope.row.deviceStatus" />
             </template>
@@ -209,6 +326,25 @@
             prop="assetProperty"
             min-width="110"
           >
+            <template #header>
+              <div class="flex items-center justify-center pb-[1px]">
+                <el-dropdown @command="handleAssetProperty">
+                  <span class="text-[#ad9399] text-[12px] cursor-pointer flex items-center">
+                    <span> 资产性质 </span> <Icon icon="ep:arrow-down" />
+                  </span>
+                  <template #dropdown>
+                    <el-dropdown-menu>
+                      <el-dropdown-item
+                        v-for="item in getStrDictOptions(DICT_TYPE.PMS_ASSET_PROPERTY)"
+                        :key="item.label"
+                        :command="item.value"
+                        >{{ item.label }}</el-dropdown-item
+                      >
+                    </el-dropdown-menu>
+                  </template>
+                </el-dropdown>
+              </div>
+            </template>
             <template #default="scope">
               <dict-tag :type="DICT_TYPE.PMS_ASSET_PROPERTY" :value="scope.row.assetProperty" />
             </template>
@@ -219,6 +355,9 @@
             prop="assetClassName"
             min-width="170"
           />
+
+          <el-table-column label="车牌号" align="center" prop="carNo" min-width="170" />
+
           <el-table-column
             :label="t('deviceForm.mfg')"
             align="center"
@@ -255,6 +394,7 @@
             prop="assetOwnership"
             min-width="170"
           />
+          <el-table-column label="所在地点" align="center" prop="address" min-width="170" />
           <el-table-column
             :label="t('operationFill.operation')"
             align="center"
@@ -308,6 +448,7 @@ import { buildSortingField } from '@/utils'
 import { defaultProps, handleTree } from '@/utils/tree'
 import * as ProductClassifyApi from '@/api/pms/productclassify'
 import { useRefreshStore } from '@/store/modules/pms/refreshStore'
+import { Search } from '@element-plus/icons-vue'
 
 /** 设备台账 列表 */
 defineOptions({ name: 'IotDevicePms' })
@@ -357,7 +498,10 @@ const queryParams = reactive({
   creator: undefined,
   sortingFields: [],
   assetClass: undefined,
-  yfDeviceCode: undefined
+  yfDeviceCode: undefined,
+  carNo: undefined,
+  deviceNo: undefined,
+  assetClassName: undefined
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
@@ -372,6 +516,16 @@ const shou = (tree) => {
   }
 }
 
+const handleCommand = (command) => {
+  queryParams.deviceStatus = command
+  getList()
+}
+
+const handleAssetProperty = (command) => {
+  queryParams.assetProperty = command
+  getList()
+}
+
 const handleSortChange = (params: any) => {
   //console.log(`排序字段: ${prop}, 排序方式: ${order}`);
   queryParams.sortingFields = []
@@ -484,4 +638,9 @@ onMounted(async () => {
   top: 0px;
   z-index: 2000;
 }
+
+::v-deep .el-tooltip__trigger {
+  border: none !important;
+  outline: none !important;
+}
 </style>

+ 194 - 130
src/views/pms/iotprojecttask/index.vue

@@ -50,6 +50,15 @@
           class="!w-240px"
         />
       </el-form-item>
+      <el-form-item label="施工队伍" prop="deptName">
+        <el-input
+          v-model="queryParams.deptName"
+          placeholder="请输入施工队伍"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </el-form-item>
       <el-form-item label="井号" prop="wellName">
         <el-input
           v-model="queryParams.wellName"
@@ -109,26 +118,70 @@
   </ContentWrap>
 
   <!-- 列表 -->
-  <ContentWrap  ref="tableContainerRef">
+  <ContentWrap ref="tableContainerRef">
     <div class="table-container">
-      <el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" style="width: 100%" :cell-style="{padding: '5px'}">
-        <el-table-column :label="t('iotDevice.serial')" :width="columnWidths.serial" align="center" v-if="false">
+      <el-table
+        ref="tableRef"
+        v-loading="loading"
+        :data="list"
+        :stripe="true"
+        style="width: 100%"
+        :cell-style="{ padding: '5px' }"
+      >
+        <el-table-column
+          :label="t('iotDevice.serial')"
+          :width="columnWidths.serial"
+          align="center"
+          v-if="false"
+        >
           <template #default="scope">
             {{ scope.$index + 1 }}
           </template>
         </el-table-column>
-        <el-table-column label="客户名称" align="center" prop="manufactureName" :width="columnWidths.manufactureName" show-overflow-tooltip/>
-        <el-table-column label="合同名称" align="center" prop="contractName" :width="columnWidths.contractName" show-overflow-tooltip/>
-        <el-table-column label="合同编号" align="center" prop="contractCode" :width="columnWidths.contractCode" />
-        <el-table-column label="井号" align="center" prop="wellName" :width="columnWidths.wellName" />
+        <el-table-column
+          label="客户名称"
+          align="center"
+          prop="manufactureName"
+          :width="columnWidths.manufactureName"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          label="合同名称"
+          align="center"
+          prop="contractName"
+          :width="columnWidths.contractName"
+          show-overflow-tooltip
+        />
+        <el-table-column
+          label="合同编号"
+          align="center"
+          prop="contractCode"
+          :width="columnWidths.contractCode"
+        />
+        <el-table-column
+          label="井号"
+          align="center"
+          prop="wellName"
+          :width="columnWidths.wellName"
+        />
         <!-- <el-table-column label="井型/井别" align="center" prop="wellType" />
         <el-table-column :label="t('project.wellType')" align="center" prop="wellType" :width="columnWidths.wellType">
           <template #default="scope">
             <dict-tag :type="DICT_TYPE.PMS_PROJECT_WELL_TYPE" :value="scope.row.wellType" />
           </template>
         </el-table-column> -->
-        <el-table-column label="施工地点" align="center" prop="location" :width="columnWidths.location" />
-        <el-table-column label="施工队伍" align="center" prop="deptNames" :width="columnWidths.deptNames" />
+        <el-table-column
+          label="施工地点"
+          align="center"
+          prop="location"
+          :width="columnWidths.location"
+        />
+        <el-table-column
+          label="施工队伍"
+          align="center"
+          prop="deptNames"
+          :width="columnWidths.deptNames"
+        />
         <!-- <el-table-column :label="t('project.technology')" align="center" prop="technique" :width="columnWidths.technique">
           <template #default="scope">
             <dict-tag :type="DICT_TYPE.PMS_PROJECT_TECHNOLOGY" :value="scope.row.technique" />
@@ -157,7 +210,7 @@
             <el-button
               link
               type="primary"
-              @click="openForm('update', scope.row.id,scope.row.projectId)"
+              @click="openForm('update', scope.row.id, scope.row.projectId)"
               v-hasPermi="['rq:iot-project-task:update']"
             >
               编辑
@@ -246,9 +299,7 @@
       </el-table-column>
       <el-table-column label="操作" width="100" align="center">
         <template #default="scope">
-          <el-button link type="danger" @click="removeRow(scope.$index)">
-            删除
-          </el-button>
+          <el-button link type="danger" @click="removeRow(scope.$index)"> 删除 </el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -256,25 +307,22 @@
     <template #footer>
       <span class="dialog-footer">
         <el-button @click="planDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="savePlan" :loading="saveLoading">
-          保存
-        </el-button>
+        <el-button type="primary" @click="savePlan" :loading="saveLoading"> 保存 </el-button>
       </span>
     </template>
   </el-dialog>
-
 </template>
 
 <script setup lang="ts">
 import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotProjectTaskScheduleApi } from '@/api/pms/iotprojecttaskschedule'
-import { IotProjectTaskApi, IotProjectTaskVO} from '@/api/pms/iotprojecttask'
+import { IotProjectTaskApi, IotProjectTaskVO } from '@/api/pms/iotprojecttask'
 import dayjs from 'dayjs'
 import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
-import { ref, reactive, onMounted, computed, nextTick, watch, onUnmounted } from 'vue'
+import { ref, reactive, onMounted, nextTick, watch, onUnmounted } from 'vue'
 import { useRouter } from 'vue-router'
-import * as DeptApi from "@/api/system/dept"; // 引入部门API
+import * as DeptApi from '@/api/system/dept' // 引入部门API
 
 /** 项目信息任务拆分 列表 */
 defineOptions({ name: 'IotProjectTask' })
@@ -301,6 +349,7 @@ const queryParams = reactive({
   manufactureName: '',
   platformFlag: '',
   remark: undefined,
+  deptName: undefined
 })
 
 const dictQueryParams = reactive({
@@ -315,7 +364,7 @@ const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
 const { push } = useRouter() // 路由跳转
 
-const COMPLETED_STATUS = 'wg';
+const COMPLETED_STATUS = 'wg'
 
 // 表格引用
 const tableRef = ref()
@@ -340,29 +389,31 @@ const columnWidths = ref({
 
 // 计算文本宽度
 const getTextWidth = (text: string, fontSize = 14) => {
-  const span = document.createElement('span');
-  span.style.visibility = 'hidden';
-  span.style.position = 'absolute';
-  span.style.whiteSpace = 'nowrap';
-  span.style.fontSize = `${fontSize}px`;
-  span.style.fontFamily = 'inherit';
-  span.innerText = text;
-
-  document.body.appendChild(span);
-  const width = span.offsetWidth;
-  document.body.removeChild(span);
-
-  return width;
-};
+  const span = document.createElement('span')
+  span.style.visibility = 'hidden'
+  span.style.position = 'absolute'
+  span.style.whiteSpace = 'nowrap'
+  span.style.fontSize = `${fontSize}px`
+  span.style.fontFamily = 'inherit'
+  span.innerText = text
+
+  document.body.appendChild(span)
+  const width = span.offsetWidth
+  document.body.removeChild(span)
+
+  return width
+}
 
 // 计划相关状态
 const planDialogVisible = ref(false)
 // const planList = ref<Array<{name: string, value: string, startTime: string, endTime: string}>>([])
-const planList = ref<Array<{id?: number, status: string, startTime: string, endTime: string, showEndTime: boolean}>>([])
+const planList = ref<
+  Array<{ id?: number; status: string; startTime: string; endTime: string; showEndTime: boolean }>
+>([])
 const saveLoading = ref(false)
 const currentRow = ref<IotProjectTaskVO | null>(null)
 const workProgressDictOptions = ref<any[]>([]) // 施工进度字典选项
-const companyDeptList = ref<any[]>([])  // 在公司级部门列表
+const companyDeptList = ref<any[]>([]) // 在公司级部门列表
 const wellTypeDictOptions = ref<any[]>([]) // 井型字典选项
 const technologyDictOptions = ref<any[]>([]) // 施工工艺字典选项
 
@@ -389,19 +440,19 @@ const getTechnologyDictOptions = async () => {
 /** 时间戳转换为日期时间字符串(使用dayjs处理) */
 const timestampToDateTime = (timestamp: number | string | null | undefined): string => {
   if (timestamp === null || timestamp === undefined || timestamp === '') {
-    return '';
+    return ''
   }
   // 转换为数字
-  let ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp;
+  let ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp
 
   // 检查是否为有效数字
   if (isNaN(ts)) {
-    console.warn('无效的时间戳:', timestamp);
-    return '';
+    console.warn('无效的时间戳:', timestamp)
+    return ''
   }
   // 如果时间戳是秒级,转换为毫秒级
   if (ts < 1000000000000) {
-    ts *= 1000;
+    ts *= 1000
   }
   return dayjs(ts).format('YYYY-MM-DD HH:mm')
 }
@@ -425,12 +476,14 @@ const openPlanDialog = async (row: IotProjectTaskVO) => {
     await getWorkProgressDictOptions()
 
     // 获取已有计划数据
-    const taskSchedules = await IotProjectTaskScheduleApi.getIotProjectTaskSchedules({ taskId: row.id })
+    const taskSchedules = await IotProjectTaskScheduleApi.getIotProjectTaskSchedules({
+      taskId: row.id
+    })
 
     if (taskSchedules && taskSchedules.length > 0) {
       // 如果有数据,则使用接口返回的数据初始化表格
       planList.value = taskSchedules.map((plan: any) => {
-        const statusNum = plan.status;
+        const statusNum = plan.status
         return {
           id: plan.id,
           status: statusNum,
@@ -451,17 +504,17 @@ const openPlanDialog = async (row: IotProjectTaskVO) => {
 
 /** 判断某一行是否应该显示结束时间列 */
 const rowShowEndTime = (row) => {
-  return row.showEndTime;
+  return row.showEndTime
 }
 
 /** 处理施工状态变化 */
 const onStatusChange = (row) => {
   // 当状态变为“完工”时,隐藏结束时间列并清空结束时间;否则显示
   if (row.status === COMPLETED_STATUS) {
-    row.showEndTime = false;
-    row.endTime = ''; // 清空结束时间
+    row.showEndTime = false
+    row.endTime = '' // 清空结束时间
   } else {
-    row.showEndTime = true;
+    row.showEndTime = true
   }
 }
 
@@ -495,12 +548,13 @@ const savePlan = async () => {
     }
 
     // 准备提交数据
-    const submitData = planList.value.map(item => ({
+    const submitData = planList.value.map((item) => ({
       id: item.id, // 更新时使用
       taskId: currentRow.value?.id,
       status: item.status,
       startTime: item.startTime ? new Date(item.startTime).getTime() : null,
-      endTime: (item.status !== COMPLETED_STATUS && item.endTime) ? new Date(item.endTime).getTime() : null
+      endTime:
+        item.status !== COMPLETED_STATUS && item.endTime ? new Date(item.endTime).getTime() : null
     }))
 
     // 调用保存接口
@@ -526,8 +580,8 @@ const getList = async () => {
 
     // 获取数据后计算列宽
     nextTick(() => {
-      calculateColumnWidths();
-    });
+      calculateColumnWidths()
+    })
   } finally {
     loading.value = false
   }
@@ -535,111 +589,118 @@ const getList = async () => {
 
 // 计算列宽度
 const calculateColumnWidths = () => {
-  const MIN_WIDTH = 80; // 最小列宽
-  const PADDING = 25; // 列内边距
-  const FLEXIBLE_COLUMNS = ['contractCode', 'wellName', 'location']; // 可伸缩列
+  const MIN_WIDTH = 80 // 最小列宽
+  const PADDING = 25 // 列内边距
+  const FLEXIBLE_COLUMNS = ['contractCode', 'wellName', 'location'] // 可伸缩列
 
   // 确保表格容器存在
-  if (!tableContainerRef.value?.$el) return;
+  if (!tableContainerRef.value?.$el) return
 
-  const container = tableContainerRef.value.$el;
-  const containerWidth = container.clientWidth;
+  const container = tableContainerRef.value.$el
+  const containerWidth = container.clientWidth
 
   // 固定列的宽度
   const FIXED_COLUMNS = {
     manufactureName: 200,
     contractName: 200
-  };
+  }
 
   // 1. 计算所有列的最小宽度
-  const minWidths: Record<string, number> = {};
-  let totalMinWidth = 0;
+  const minWidths: Record<string, number> = {}
+  let totalMinWidth = 0
 
   // 设置固定列的宽度
-  Object.keys(FIXED_COLUMNS).forEach(key => {
-    minWidths[key] = FIXED_COLUMNS[key];
-    totalMinWidth += FIXED_COLUMNS[key];
-  });
+  Object.keys(FIXED_COLUMNS).forEach((key) => {
+    minWidths[key] = FIXED_COLUMNS[key]
+    totalMinWidth += FIXED_COLUMNS[key]
+  })
 
   // 计算列最小宽度的函数
   const calculateColumnMinWidth = (key: string, label: string, getValue: Function) => {
     // 如果是固定列,跳过计算
-    if (FIXED_COLUMNS[key]) return;
+    if (FIXED_COLUMNS[key]) return
 
-    const headerWidth = getTextWidth(label) * 1.2;
-    let contentMaxWidth = 0;
+    const headerWidth = getTextWidth(label) * 1.2
+    let contentMaxWidth = 0
 
     // 计算内容最大宽度
     list.value.forEach((row, index) => {
-      const text = String(getValue ? getValue(row, index) : (row[key] || ''));
-      const textWidth = getTextWidth(text);
-      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
-    });
-
-    const minWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING;
-    minWidths[key] = minWidth;
-    totalMinWidth += minWidth;
-    return minWidth;
-  };
+      const text = String(getValue ? getValue(row, index) : row[key] || '')
+      const textWidth = getTextWidth(text)
+      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth
+    })
+
+    const minWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH) + PADDING
+    minWidths[key] = minWidth
+    totalMinWidth += minWidth
+    return minWidth
+  }
 
   // 计算各列最小宽度
-  calculateColumnMinWidth('serial', t('iotDevice.serial'), (row: any, index: number) => `${index + 1}`);
+  calculateColumnMinWidth(
+    'serial',
+    t('iotDevice.serial'),
+    (row: any, index: number) => `${index + 1}`
+  )
   // calculateColumnMinWidth('manufactureName', '客户名称', (row: any) => row.manufactureName);
   // calculateColumnMinWidth('contractName', '合同名称', (row: any) => row.contractName);
-  calculateColumnMinWidth('contractCode', '合同编号', (row: any) => row.contractCode);
-  calculateColumnMinWidth('wellName', '井号', (row: any) => row.wellName);
+  calculateColumnMinWidth('contractCode', '合同编号', (row: any) => row.contractCode)
+  calculateColumnMinWidth('wellName', '井号', (row: any) => row.wellName)
   calculateColumnMinWidth('wellType', t('project.wellType'), (row: any) => {
-    const dict = wellTypeDictOptions.value.find(d => d.value === row.wellType);
-    return dict ? dict.label : '';
-  });
-  calculateColumnMinWidth('location', '施工地点', (row: any) => row.location);
-  calculateColumnMinWidth('deptNames', '施工队伍', (row: any) => row.deptNames);
+    const dict = wellTypeDictOptions.value.find((d) => d.value === row.wellType)
+    return dict ? dict.label : ''
+  })
+  calculateColumnMinWidth('location', '施工地点', (row: any) => row.location)
+  calculateColumnMinWidth('deptNames', '施工队伍', (row: any) => row.deptNames)
   calculateColumnMinWidth('technique', t('project.technology'), (row: any) => {
-    const dict = technologyDictOptions.value.find(d => d.value === row.technique);
-    return dict ? dict.label : '';
-  });
-  calculateColumnMinWidth('workloadDesign', '设计工作量', (row: any) => row.workloadDesign);
-  calculateColumnMinWidth('createTime', '创建时间', (row: any) => dateFormatter(null, null, row.createTime));
+    const dict = technologyDictOptions.value.find((d) => d.value === row.technique)
+    return dict ? dict.label : ''
+  })
+  calculateColumnMinWidth('workloadDesign', '设计工作量', (row: any) => row.workloadDesign)
+  calculateColumnMinWidth('createTime', '创建时间', (row: any) =>
+    dateFormatter(null, null, row.createTime)
+  )
 
   // 操作列固定宽度
-  minWidths.operation = 200;
-  totalMinWidth += 200;
+  minWidths.operation = 200
+  totalMinWidth += 200
 
   // 2. 计算可伸缩列最终宽度
-  const newWidths: Record<string, string> = {};
-  const availableWidth = containerWidth - 17; // 减去滚动条宽度
+  const newWidths: Record<string, string> = {}
+  const availableWidth = containerWidth - 17 // 减去滚动条宽度
 
   // 应用最小宽度到所有列
-  Object.keys(minWidths).forEach(key => {
+  Object.keys(minWidths).forEach((key) => {
     if (FIXED_COLUMNS[key]) {
-      newWidths[key] = `${FIXED_COLUMNS[key]}px`;
+      newWidths[key] = `${FIXED_COLUMNS[key]}px`
     } else {
-      newWidths[key] = `${minWidths[key]}px`;
+      newWidths[key] = `${minWidths[key]}px`
     }
-  });
+  })
 
   // 计算可伸缩列需要的宽度
   if (totalMinWidth < availableWidth) {
     // 有剩余空间:按比例分配给可伸缩列
-    const extraSpace = availableWidth - totalMinWidth;
-    const flexibleColumnCount = FLEXIBLE_COLUMNS.length;
-    const spacePerColumn = Math.floor(extraSpace / flexibleColumnCount);
-
-    FLEXIBLE_COLUMNS.forEach(key => {
-      if (!FIXED_COLUMNS[key]) { // 确保不是固定列
-        newWidths[key] = `${minWidths[key] + spacePerColumn}px`;
+    const extraSpace = availableWidth - totalMinWidth
+    const flexibleColumnCount = FLEXIBLE_COLUMNS.length
+    const spacePerColumn = Math.floor(extraSpace / flexibleColumnCount)
+
+    FLEXIBLE_COLUMNS.forEach((key) => {
+      if (!FIXED_COLUMNS[key]) {
+        // 确保不是固定列
+        newWidths[key] = `${minWidths[key] + spacePerColumn}px`
       }
-    });
+    })
   }
 
   // 3. 更新列宽配置
-  columnWidths.value = newWidths;
+  columnWidths.value = newWidths
 
   // 4. 触发表格重新布局
   nextTick(() => {
-    tableRef.value?.doLayout();
-  });
-};
+    tableRef.value?.doLayout()
+  })
+}
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
@@ -655,11 +716,11 @@ const resetQuery = () => {
 
 /** 添加/修改操作 */
 const formRef = ref()
-const openForm = (type: string, id?: number,projectId?:number) => {
-  if(id===undefined){
-    push({ name: 'IotProjectTaskInfo', params: { type} })
-  }else{
-    push({ name: 'IotProjectTaskInfo', params: { type,id,projectId} })
+const openForm = (type: string, id?: number, projectId?: number) => {
+  if (id === undefined) {
+    push({ name: 'IotProjectTaskInfo', params: { type } })
+  } else {
+    push({ name: 'IotProjectTaskInfo', params: { type, id, projectId } })
   }
 }
 
@@ -692,7 +753,7 @@ const handleExport = async () => {
 }
 
 // 声明 ResizeObserver 实例
-let resizeObserver: ResizeObserver | null = null;
+let resizeObserver: ResizeObserver | null = null
 
 /** 初始化 **/
 onMounted(async () => {
@@ -709,33 +770,36 @@ onMounted(async () => {
   if (tableContainerRef.value?.$el) {
     resizeObserver = new ResizeObserver(() => {
       // 使用防抖避免频繁触发
-      clearTimeout((window as any).resizeTimer);
-      (window as any).resizeTimer = setTimeout(() => {
-        calculateColumnWidths();
-      }, 100);
-    });
-    resizeObserver.observe(tableContainerRef.value.$el);
+      clearTimeout((window as any).resizeTimer)
+      ;(window as any).resizeTimer = setTimeout(() => {
+        calculateColumnWidths()
+      }, 100)
+    })
+    resizeObserver.observe(tableContainerRef.value.$el)
   }
 })
 
 onUnmounted(() => {
   // 清除 ResizeObserver
   if (resizeObserver && tableContainerRef.value?.$el) {
-    resizeObserver.unobserve(tableContainerRef.value.$el);
-    resizeObserver = null;
+    resizeObserver.unobserve(tableContainerRef.value.$el)
+    resizeObserver = null
   }
 
   // 清除定时器
   if ((window as any).resizeTimer) {
-    clearTimeout((window as any).resizeTimer);
+    clearTimeout((window as any).resizeTimer)
   }
 })
 
 // 监听列表数据变化重新计算列宽
-watch(list, () => {
-  nextTick(calculateColumnWidths)
-}, { deep: true })
-
+watch(
+  list,
+  () => {
+    nextTick(calculateColumnWidths)
+  },
+  { deep: true }
+)
 </script>
 
 <style scoped>

+ 29 - 39
src/views/pms/iotrddailyreport/index.vue

@@ -72,7 +72,6 @@
           </el-form-item>
         </el-form>
       </ContentWrap>
-
       <!-- 列表 -->
       <ContentWrap ref="tableContainerRef">
         <el-table
@@ -100,13 +99,7 @@
             :min-width="columnWidths.deptName.width"
             resizable
           />
-          <el-table-column
-            label="项目"
-            align="center"
-            prop="contractName"
-            :min-width="columnWidths.contractName.width"
-            resizable
-          />
+
           <el-table-column
             label="任务"
             align="center"
@@ -114,13 +107,7 @@
             :min-width="columnWidths.taskName.width"
             resizable
           />
-          <el-table-column
-            label="时间节点"
-            align="center"
-            prop="timeRange"
-            :min-width="columnWidths.timeRange.width"
-            resizable
-          />
+
           <el-table-column
             :label="t('project.status')"
             align="center"
@@ -132,20 +119,20 @@
               <dict-tag :type="DICT_TYPE.PMS_PROJECT_RD_STATUS" :value="scope.row.rdStatus" />
             </template>
           </el-table-column>
+
           <el-table-column
-            label="项目"
+            label="当日生产动态"
             align="center"
-            prop="contractName"
-            :width="columnWidths.contractName"
-            v-if="false"
+            prop="productionStatus"
+            :min-width="columnWidths.productionStatus.width"
+            resizable
           />
-
           <el-table-column
-            label="时间节点"
+            label="下步工作计划"
             align="center"
-            prop="timeRange"
-            :width="columnWidths.timeRange"
-            v-if="false"
+            prop="nextPlan"
+            :min-width="columnWidths.nextPlan.width"
+            fixed-width
           />
 
           <!--
@@ -220,20 +207,7 @@
             :min-width="columnWidths.dailyFuel.width"
             resizable
           />
-          <el-table-column
-            label="当日生产动态"
-            align="center"
-            prop="productionStatus"
-            :min-width="columnWidths.productionStatus.width"
-            resizable
-          />
-          <el-table-column
-            label="下步工作计划"
-            align="center"
-            prop="nextPlan"
-            :min-width="columnWidths.nextPlan.width"
-            fixed-width
-          />
+
           <el-table-column
             label="外租设备"
             align="center"
@@ -255,6 +229,20 @@
             :min-width="columnWidths.faultDowntime.width"
             resizable
           />
+          <el-table-column
+            label="项目"
+            align="center"
+            prop="contractName"
+            :min-width="columnWidths.contractName.width"
+            resizable
+          />
+          <el-table-column
+            label="时间节点"
+            align="center"
+            prop="timeRange"
+            :min-width="columnWidths.timeRange.width"
+            resizable
+          />
 
           <el-table-column label="操作" align="center" min-width="120px" fixed="right">
             <template #default="scope">
@@ -294,7 +282,7 @@
 </template>
 
 <script setup lang="ts">
-import { dateFormatter2 } from '@/utils/formatTime'
+import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotRdDailyReportApi, IotRdDailyReportVO } from '@/api/pms/iotrddailyreport'
 import IotRdDailyReportForm from './IotRdDailyReportForm.vue'
@@ -612,6 +600,8 @@ const calculateColumnWidths = useDebounceFn(() => {
         ]
       ) + 'px'
 
+    console.log('width :>> ', width)
+
     columnWidths.value[prop].width = width
   })
 }, 1000)

+ 1 - 1
src/views/pms/iotrydailyreport/index.vue

@@ -88,7 +88,7 @@
         <div class="color-legend">
           <div class="legend-item">
             <span class="color-indicator red"></span>
-            <span>当日油耗大于15升&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;红色预警</span>
+            <span>当日油耗大于9000升&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;红色预警</span>
           </div>
           <div class="legend-item">
             <span class="color-indicator orange"></span>

+ 19 - 4
src/views/pms/iotrydailyreport/summary.vue

@@ -47,6 +47,13 @@ const totalWorkKeys: [string, string, string, string, number][] = [
     0
   ],
   ['notReported', '个', '未填报', 'i-material-symbols:cancel-outline-rounded text-rose', 0],
+  [
+    'averageFuelConsumption',
+    '升',
+    '平均油耗',
+    'i-material-symbols:directions-car-outline-rounded text-sky',
+    2
+  ],
   [
     'totalFuelConsumption',
     '升',
@@ -70,7 +77,8 @@ const totalWork = ref({
   notReported: 0,
   totalFuelConsumption: 0,
   totalPowerConsumption: 0,
-  totalFootage: 0
+  totalFootage: 0,
+  averageFuelConsumption: 0
 })
 
 const totalLoading = ref(false)
@@ -101,7 +109,8 @@ const getTotal = useDebounceFn(async () => {
       ...res2,
       totalPowerConsumption: (res2.totalPowerConsumption || 0) / 1000,
       totalGasInjection: (res2.totalGasInjection || 0) / 10000,
-      totalFuelConsumption: res2.totalFuelConsumption || 0
+      totalFuelConsumption: res2.totalFuelConsumption || 0,
+      averageFuelConsumption: res2.averageFuelConsumption || 0
     }
   } finally {
     totalLoading.value = false
@@ -118,6 +127,7 @@ interface List {
   cumulativeFuelConsumption: number | null
   transitTime: number | null
   nonProductiveTime: number | null
+  averageFuelConsumption: number | null
 }
 
 const list = ref<List[]>([])
@@ -142,6 +152,10 @@ const columns = (type: string) => {
       label: '累计油耗(升)',
       prop: 'cumulativeFuelConsumption'
     },
+    {
+      label: '平均油耗(升)',
+      prop: 'averageFuelConsumption'
+    },
     {
       label: '平均时效(%)',
       prop: 'transitTime'
@@ -178,7 +192,8 @@ const getList = useDebounceFn(async () => {
         name: type === '2' ? projectDeptName : teamName,
         ...other,
         cumulativePowerConsumption: ((other.cumulativePowerConsumption || 0) / 1000).toFixed(2),
-        cumulativeFuelConsumption: (other.cumulativeFuelConsumption || 0).toFixed(2)
+        cumulativeFuelConsumption: (other.cumulativeFuelConsumption || 0).toFixed(2),
+        averageFuelConsumption: (other.averageFuelConsumption || 0).toFixed(2)
       })
     )
   } finally {
@@ -527,7 +542,7 @@ const tolist = (id: number, non: boolean = false) => {
           <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
         </el-form-item>
       </el-form>
-      <div class="grid grid-cols-6 gap-8">
+      <div class="grid grid-cols-7 gap-8">
         <div
           v-for="info in totalWorkKeys"
           :key="info[0]"

+ 21 - 0
src/views/pms/iotrydailyreport/xapproval.vue

@@ -585,6 +585,27 @@ onMounted(() => {
   }
 }
 
+:deep(.warning-input) {
+  .el-input__inner {
+    color: red !important;
+    -webkit-text-fill-color: red !important;
+  }
+}
+
+:deep(.blue-input) {
+  .el-input__inner {
+    color: blue !important;
+    -webkit-text-fill-color: blue !important;
+  }
+}
+
+:deep(.orange-input) {
+  .el-input__inner {
+    color: orange !important;
+    -webkit-text-fill-color: orange !important;
+  }
+}
+
 :deep(.el-input-number) {
   width: 100%;
 }

+ 21 - 0
src/views/pms/iotrydailyreport/xfill.vue

@@ -585,6 +585,27 @@ onMounted(() => {
   }
 }
 
+:deep(.warning-input) {
+  .el-input__inner {
+    color: red !important;
+    -webkit-text-fill-color: red !important;
+  }
+}
+
+:deep(.blue-input) {
+  .el-input__inner {
+    color: blue !important;
+    -webkit-text-fill-color: blue !important;
+  }
+}
+
+:deep(.orange-input) {
+  .el-input__inner {
+    color: orange !important;
+    -webkit-text-fill-color: orange !important;
+  }
+}
+
 :deep(.el-input-number) {
   width: 100%;
 }

+ 1 - 1
src/views/pms/iotrydailyreport/xjindex.vue

@@ -100,7 +100,7 @@
           </div>
           <div class="legend-item">
             <span class="color-indicator yellow"></span>
-            <span>当日油耗大于5升&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;蓝色预警</span>
+            <span>当日油耗大于3500升&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;蓝色预警</span>
           </div>
         </div>
       </ContentWrap>

+ 19 - 4
src/views/pms/iotrydailyreport/xsummary.vue

@@ -54,6 +54,13 @@ const totalWorkKeys: [string, string, string, string, number][] = [
     'i-material-symbols:directions-car-outline-rounded text-sky',
     2
   ],
+  [
+    'averageFuelConsumption',
+    '升',
+    '平均油耗',
+    'i-material-symbols:directions-car-outline-rounded text-sky',
+    2
+  ],
   [
     'totalPowerConsumption',
     'MWh',
@@ -72,7 +79,8 @@ const totalWork = ref({
   totalFuelConsumption: 0,
   totalPowerConsumption: 0,
   constructionWells: 0,
-  completedWells: 0
+  completedWells: 0,
+  averageFuelConsumption: 0
 })
 
 const totalLoading = ref(false)
@@ -103,7 +111,8 @@ const getTotal = useDebounceFn(async () => {
       completedWells: 0,
       ...res2,
       totalPowerConsumption: (res2.totalPowerConsumption || 0) / 1000,
-      totalFuelConsumption: res2.totalFuelConsumption || 0
+      totalFuelConsumption: res2.totalFuelConsumption || 0,
+      averageFuelConsumption: res2.averageFuelConsumption || 0
     }
   } finally {
     totalLoading.value = false
@@ -120,6 +129,7 @@ interface List {
   cumulativeFuelConsumption: number | null
   transitTime: number | null
   nonProductiveTime: number | null
+  averageFuelConsumption: number | null
 }
 
 const list = ref<List[]>([])
@@ -148,6 +158,10 @@ const columns = (type: string) => {
       label: '累计油耗(升)',
       prop: 'cumulativeFuelConsumption'
     },
+    {
+      label: '平均油耗(升)',
+      prop: 'averageFuelConsumption'
+    },
     {
       label: '平均时效(%)',
       prop: 'transitTime'
@@ -184,7 +198,8 @@ const getList = useDebounceFn(async () => {
         name: type === '2' ? projectDeptName : teamName,
         ...other,
         cumulativePowerConsumption: ((other.cumulativePowerConsumption || 0) / 1000).toFixed(2),
-        cumulativeFuelConsumption: (other.cumulativeFuelConsumption || 0).toFixed(2)
+        cumulativeFuelConsumption: (other.cumulativeFuelConsumption || 0).toFixed(2),
+        averageFuelConsumption: (other.averageFuelConsumption || 0).toFixed(2)
       })
     )
   } finally {
@@ -525,7 +540,7 @@ const tolist = (id: number, non: boolean = false) => {
           <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
         </el-form-item>
       </el-form>
-      <div class="grid grid-cols-7 gap-8">
+      <div class="grid grid-cols-8 gap-8">
         <div
           v-for="info in totalWorkKeys"
           :key="info[0]"

+ 3 - 1
src/views/report-statistics/inspection_order/index.vue

@@ -595,12 +595,14 @@ const detailQueryParams = ref({
   pageNo: 1,
   pageSize: 20,
   status: '异常',
-  deviceCode: ''
+  deviceCode: '',
+  createTime: []
 })
 const goDetail = async (code: string) => {
   // 抽屉打开设备详情
   drawerVisible.value = true
   detailQueryParams.value.deviceCode = code
+  detailQueryParams.value.createTime = queryParams.createTime
 
   const data = await IotInspectOrderDetailApi.getIotInspectItemStatusPage(detailQueryParams.value)
   deviceDetail.value = data.list

+ 1 - 1
src/views/report-statistics/ry-daily-report.vue

@@ -408,7 +408,7 @@ function cellStyle(data: {
   if (column.property === 'dailyFuel') {
     const originalValue = row.dailyFuel ?? 0
 
-    if (originalValue > 15)
+    if (originalValue > 9000)
       return {
         color: 'red',
         fontWeight: 'bold'