yanghao преди 1 ден
родител
ревизия
352832be48

+ 1 - 1
src/api/pms/iotrddailyreport/index.ts

@@ -71,7 +71,7 @@ export const IotRdDailyReportApi = {
   },
 
   // 新增瑞都日报
-  createIotRdDailyReport: async (data: IotRdDailyReportVO) => {
+  createIotRdDailyReport: async (data: any) => {
     return await request.post({ url: `/pms/iot-rd-daily-report/create`, data })
   },
 

+ 4 - 2
src/api/system/user/index.ts

@@ -34,7 +34,7 @@ export const companyDeptsEmployee = (params: PageParam) => {
 }
 
 // 获取选择的部门包含的子部门下的所有人员
-export const selectedDeptsEmployee = (params: PageParam) => {
+export const selectedDeptsEmployee = (params: any) => {
   return request.get({ url: '/system/user/selectedDeptsEmployee', params })
 }
 
@@ -94,7 +94,9 @@ export const getDeptUsers = (id): Promise<UserVO[]> => {
   return request.get({ url: '/system/user/dept/users?userId=' + id })
 }
 export const getMaintainUsers = (id, businessId): Promise<UserVO[]> => {
-  return request.get({ url: '/rq/iot-maintain/maintain/users?userId=' + id + '&businessId=' + businessId})
+  return request.get({
+    url: '/rq/iot-maintain/maintain/users?userId=' + id + '&businessId=' + businessId
+  })
 }
 export const getDeptUsersByDeptId = (id): Promise<UserVO[]> => {
   return request.get({ url: '/system/user/dept/users/id?userId=' + id })

+ 8 - 5
src/utils/useMqtt.ts

@@ -45,12 +45,12 @@ export function useMqtt() {
 
       client.value.on('connect', () => {
         isConnected.value = true
-        ElMessage.success('MQTT 连接成功')
+        // ElMessage.success('MQTT 连接成功')
       })
 
       client.value.on('error', (err) => {
         console.error('MQTT Error:', err)
-        ElMessage.error(`MQTT 错误: ${err.message}`)
+        ElMessage.error('服务器连接异常,请检查网络或稍后重试')
         isConnected.value = false
       })
 
@@ -74,7 +74,8 @@ export function useMqtt() {
         }
       })
     } catch (err) {
-      ElMessage.error(`MQTT 初始化异常: ${(err as Error).message}`)
+      console.error('MQTT 初始化异常:', err)
+      ElMessage.error('系统连接组件加载失败,请尝试刷新页面')
       isConnected.value = false
     }
   }
@@ -83,8 +84,10 @@ export function useMqtt() {
   const subscribe = (topic: string) => {
     if (client.value && client.value.connected) {
       client.value.subscribe(topic, { qos: 0 }, (err) => {
-        if (err) ElMessage.error(`订阅失败: ${err.message}`)
-        else console.log(`已订阅: ${topic}`)
+        if (err) {
+          console.error(`订阅失败: ${err.message}`)
+          ElMessage.error('实时数据同步失败,请稍后重试')
+        } else console.log(`已订阅: ${topic}`)
       })
     }
   }

+ 0 - 35
src/views/oli-connection/monitoring-board/chart.vue

@@ -203,41 +203,6 @@ function genderIntervalArr(init: boolean = false) {
   }
 }
 
-// function genderIntervalArr(init: boolean = false) {
-//   const values: number[] = []
-
-//   for (const [key, value] of Object.entries(selectedDimension.value)) {
-//     if (value) {
-//       values.push(...(chartData.value[key]?.map((item) => item.value) ?? []))
-//     }
-//   }
-
-//   const maxVal = values.length === 0 ? 10000 : Math.max(...values)
-//   const minVal = values.length === 0 ? 0 : Math.min(...values) > 0 ? 0 : Math.min(...values)
-
-//   const maxDigits = (Math.floor(maxVal) + '').length
-//   const minDigits = minVal === 0 ? 0 : (Math.floor(Math.abs(minVal)) + '').length
-
-//   const interval = Math.max(maxDigits, minDigits)
-
-//   maxInterval.value = interval
-//   minInterval.value = minDigits
-
-//   intervalArr.value = [0]
-//   for (let i = 1; i <= interval; i++) {
-//     intervalArr.value.push(Math.pow(10, i))
-//   }
-
-//   if (!init) {
-//     chart?.setOption({
-//       yAxis: {
-//         min: -minInterval.value,
-//         max: maxInterval.value
-//       }
-//     })
-//   }
-// }
-
 function chartInit() {
   if (!chart) return
 

+ 4 - 1
src/views/oli-connection/monitoring-board/index.vue

@@ -109,7 +109,10 @@ async function loadDeviceOptions() {
       value: item.id,
       raw: item
     }))
-    handleDeviceChange(deviceOptions.value.filter((i) => i.raw.ifInline === 3).map((i) => i.value))
+    query.value.deviceCodes = deviceOptions.value
+      .filter((i) => i.raw.ifInline === 3)
+      .map((i) => i.value)
+    handleDeviceChange(query.value.deviceCodes)
   } catch (error) {
     deviceOptions.value = []
   } finally {

+ 37 - 136
src/views/oli-connection/monitoring/detail.vue

@@ -16,11 +16,12 @@ import { neonColors } from '@/utils/td-color'
 import dayjs from 'dayjs'
 import * as echarts from 'echarts'
 import { cancelAllRequests, IotStatApi } from '@/api/pms/stat'
-import { Dimensions, formatIotValue, HeaderItem, useSocketBus } from '@/utils/useSocketBus'
+import { Dimensions, formatIotValue, HeaderItem } from '@/utils/useSocketBus'
 import { rangeShortcuts } from '@/utils/formatTime'
 import { useFullscreen } from '@vueuse/core'
 import { snapdom } from '@zumer/snapdom'
 import { ElMessage } from 'element-plus'
+import { useMqtt } from '@/utils/useMqtt'
 
 const { query } = useRoute()
 
@@ -31,18 +32,17 @@ const data = ref({
   ifInline: query.ifInline === '3',
   dept: query.dept || '',
   vehicle: query.vehicle || '',
-  carOnline: query.carOnline === 'true'
+  carOnline: query.carOnline === 'true',
+  mqttUrl: query.mqttUrl || ''
 })
 
-const { open: connect, onAny, close } = useSocketBus(data.value.deviceCode as string)
-
-onAny((msg) => {
-  if (!Array.isArray(msg) || msg.length === 0) return
+const { connect, destroy, isConnected, subscribe } = useMqtt()
 
+const handleMessageUpdate = (_topic: string, data: any) => {
   const valueMap = new Map<string, number>()
 
-  for (const item of msg) {
-    const { identity, modelName, readTime, logValue } = item
+  for (const item of data) {
+    const { id: identity, value: logValue, remark } = item
 
     const value = logValue ? Number(logValue) : 0
 
@@ -50,9 +50,11 @@ onAny((msg) => {
       valueMap.set(identity, value)
     }
 
+    const modelName = dimensions.value.find((item) => item.identifier === identity)?.name
+
     if (modelName && chartData.value[modelName]) {
       chartData.value[modelName].push({
-        ts: dayjs(readTime).valueOf(),
+        ts: dayjs.unix(remark).valueOf(),
         value
       })
 
@@ -73,28 +75,35 @@ onAny((msg) => {
   updateDimensions(gatewayDimensions.value)
   updateDimensions(carDimensions.value)
 
-  // 3️⃣ 统一一次调用
   genderIntervalArr()
+}
+
+watch(isConnected, (newVal) => {
+  if (newVal) {
+    subscribe(data.value.mqttUrl as string)
+  }
 })
 
+// const { open: connect, onAny, close } = useSocketBus(data.value.deviceCode as string)
+
 // onAny((msg) => {
 //   if (!Array.isArray(msg) || msg.length === 0) return
 
-//   const valueMap = new Map<string, { value: string; isText: boolean; suffix?: string }>()
+//   const valueMap = new Map<string, number>()
 
 //   for (const item of msg) {
 //     const { identity, modelName, readTime, logValue } = item
 
-//     const { value, isText, suffix } = formatIotValue(logValue)
+//     const value = logValue ? Number(logValue) : 0
 
 //     if (identity) {
-//       valueMap.set(identity, { value, isText, suffix })
+//       valueMap.set(identity, value)
 //     }
 
 //     if (modelName && chartData.value[modelName]) {
 //       chartData.value[modelName].push({
 //         ts: dayjs(readTime).valueOf(),
-//         value: Number(value)
+//         value
 //       })
 
 //       updateSingleSeries(modelName)
@@ -105,9 +114,7 @@ onAny((msg) => {
 //     list.forEach((item) => {
 //       const v = valueMap.get(item.identifier)
 //       if (v !== undefined) {
-//         item.value = v.value
-//         item.suffix = v.suffix
-//         item.isText = v.isText
+//         item.value = v
 //       }
 //     })
 //   }
@@ -245,62 +252,6 @@ async function loadDimensions() {
   }
 }
 
-// async function updateDimensionValues() {
-//   if (!query.id) return
-
-//   try {
-//     // 1. 并行获取最新数据
-//     const [gatewayRes, carRes] = await Promise.all([
-//       IotDeviceApi.getIotDeviceTds(Number(query.id)),
-//       IotDeviceApi.getIotDeviceZHBDTds(Number(query.id))
-//     ])
-
-//     const newValueMap = new Map<string, { value: string; suffix?: string; isText?: boolean }>()
-
-//     const addToMap = (data: any[]) => {
-//       if (!data) return
-//       data.forEach((item) => {
-//         if (item.identifier) {
-//           const { value, suffix, isText } = formatIotValue(item.value)
-//           newValueMap.set(item.identifier, { value, suffix, isText })
-//         }
-//       })
-//     }
-
-//     addToMap(gatewayRes as any[])
-//     addToMap(carRes as any[])
-
-//     dimensions.value.forEach((item) => {
-//       if (newValueMap.has(item.identifier)) {
-//         const { value, suffix, isText } = newValueMap.get(item.identifier) ?? {}
-//         item.value = value ?? ''
-//         item.suffix = suffix
-//         item.isText = isText
-//       }
-//     })
-
-//     gatewayDimensions.value.forEach((item) => {
-//       if (newValueMap.has(item.identifier)) {
-//         const { value, suffix, isText } = newValueMap.get(item.identifier) ?? {}
-//         item.value = value ?? ''
-//         item.suffix = suffix
-//         item.isText = isText
-//       }
-//     })
-
-//     carDimensions.value.forEach((item) => {
-//       if (newValueMap.has(item.identifier)) {
-//         const { value, suffix, isText } = newValueMap.get(item.identifier) ?? {}
-//         item.value = value ?? ''
-//         item.suffix = suffix
-//         item.isText = isText
-//       }
-//     })
-//   } catch (error) {
-//     console.error('Failed to update dimension values:', error)
-//   }
-// }
-
 const selectedDate = ref<string[]>([
   dayjs().subtract(5, 'minute').format('YYYY-MM-DD HH:mm:ss'),
   dayjs().format('YYYY-MM-DD HH:mm:ss')
@@ -319,11 +270,6 @@ let minInterval = ref(0)
 const chartRef = ref<HTMLDivElement | null>(null)
 let chart: echarts.ECharts | null = null
 
-// const genderIntervalArrDebounce = useDebounceFn(
-//   (init: boolean = false) => genderIntervalArr(init),
-//   300
-// )
-
 function genderIntervalArr(init: boolean = false) {
   const values: number[] = []
 
@@ -516,57 +462,19 @@ function updateSingleSeries(name: string) {
 
 const lastTsMap = ref<Record<Dimensions['name'], number>>({})
 
-// async function fetchIncrementData() {
-//   for (const item of dimensions.value) {
-//     const { identifier, name } = item
-
-//     const lastTs = lastTsMap.value[name]
-//     if (!lastTs) continue
-
-//     item.response = true
-
-//     IotStatApi.getDeviceInfoChart(
-//       data.value.deviceCode,
-//       identifier,
-//       dayjs(lastTs).format('YYYY-MM-DD HH:mm:ss'),
-//       dayjs().format('YYYY-MM-DD HH:mm:ss')
-//     )
-//       .then((res) => {
-//         if (!res.length) return
-
-//         const sorted = res
-//           .sort((a, b) => a.ts - b.ts)
-//           .map((item) => ({ ts: item.ts, value: item.value }))
-//         // push 到本地
-//         chartData.value[name].push(...sorted)
-//         // 更新 lastTs
-//         lastTsMap.value[identifier] = sorted.at(-1).ts
-
-//         // 更新图表
-//         updateSingleSeries(name)
-//       })
-//       .finally(() => {
-//         item.response = false
-//       })
-//   }
-// }
+const chartLoading = ref(false)
 
-// const timer = ref<NodeJS.Timeout | null>(null)
+const token = ref('')
 
-// function startAutoFetch() {
-//   timer.value = setInterval(() => {
-//     updateDimensionValues()
-//     fetchIncrementData()
-//   }, 10000)
-// }
+async function getToken() {
+  const res = await IotDeviceApi.getToken()
 
-// function stopAutoFetch() {
-//   cancelAllRequests()
-//   if (timer.value) clearInterval(timer.value)
-//   timer.value = null
-// }
+  token.value = res
+}
 
-const chartLoading = ref(false)
+onMounted(() => {
+  getToken()
+})
 
 async function initLoadChartData(real_time: boolean = true) {
   if (!dimensions.value.length) return
@@ -603,18 +511,13 @@ async function initLoadChartData(real_time: boolean = true) {
       updateSingleSeries(name)
 
       chartLoading.value = false
-
-      // if (selectedDimension.value[name]) {
-      //   genderIntervalArr()
-      // }
     } finally {
       item.response = false
     }
   }
 
   if (real_time) {
-    // startAutoFetch()
-    connect()
+    connect(`wss://aims.deepoil.cc/mqtt`, { password: token.value }, handleMessageUpdate)
   }
 }
 
@@ -635,8 +538,7 @@ function reset() {
       dayjs().format('YYYY-MM-DD HH:mm:ss')
     ]
 
-    close()
-    // stopAutoFetch()
+    destroy()
     if (chart) chart.clear()
     initfn(false)
   })
@@ -644,7 +546,7 @@ function reset() {
 
 function handleDateChange() {
   cancelAllRequests().then(() => {
-    close()
+    destroy()
     // stopAutoFetch()
     if (chart) chart.clear()
     initfn(false, false)
@@ -705,8 +607,7 @@ const maxmin = computed(() => {
 })
 
 onUnmounted(() => {
-  // stopAutoFetch()
-  close()
+  destroy()
 
   window.removeEventListener('resize', () => {
     if (chart) chart.resize()

+ 409 - 0
src/views/pms/iotrddailyreport/create-rd-form.vue

@@ -0,0 +1,409 @@
+<script setup lang="ts">
+import { FormInstance, FormRules } from 'element-plus'
+import { Close } from '@element-plus/icons-vue'
+import * as UserApi from '@/api/system/user'
+import { useUserStore } from '@/store/modules/user'
+import { getStrDictOptions } from '@/utils/dict'
+import * as DeptApi from '@/api/system/dept'
+import { IotRdDailyReportApi } from '@/api/pms/iotrddailyreport'
+import { formatT } from '@/utils/formatTime'
+
+const deptId = useUserStore().getUser.deptId
+
+interface Props {
+  visible: boolean
+  loadList: () => void
+  isview: 'detail' | 'create' | 'time'
+  id?: number
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  visible: false,
+  isview: 'create'
+})
+
+const emits = defineEmits(['update:visible'])
+
+const NON_PROD_FIELDS = [
+  { key: 'repairTime', label: '设备故障' },
+  { key: 'selfStopTime', label: '设备保养' },
+  { key: 'accidentTime', label: '工程质量' },
+  { key: 'complexityTime', label: '技术受限' },
+  { key: 'rectificationTime', label: '生产组织' },
+  { key: 'waitingStopTime', label: '不可抗力' },
+  { key: 'partyaDesign', label: '甲方设计' },
+  { key: 'partyaPrepare', label: '甲方准备' },
+  { key: 'partyaResource', label: '甲方资源' },
+  { key: 'relocationTime', label: '生产配合' },
+  { key: 'winterBreakTime', label: '待命' },
+  { key: 'otherNptTime', label: '其他非生产时间' }
+]
+
+interface Form {
+  deptId: number
+  location: string
+  responsiblePerson: number
+  timeRange: string[]
+  rdStatus: string
+  dailyFuel: number
+  productionStatus: string
+  nextPlan: string
+  externalRental: string
+  malfunction: string
+  otherNptReason: string
+  [key: (typeof NON_PROD_FIELDS)[number]['key']]: any
+}
+
+const original = {
+  deptId,
+  timeRange: ['08:00', '08:00'],
+  ...NON_PROD_FIELDS.reduce((acc, field) => ({ ...acc, [field.key]: 0 }), {}),
+  winterBreakTime: 24
+}
+
+const formRef = ref<FormInstance>()
+
+const form = ref<Partial<Form>>({ ...original })
+
+function noProductionTimeRule() {
+  return {
+    validator: (_rule: any, _value: any, callback: any) => {
+      let totalTime = 0
+      NON_PROD_FIELDS.forEach((field) => {
+        const val = parseFloat(form.value[field.key])
+        if (!isNaN(val)) {
+          totalTime += val
+        }
+      })
+      const fixedTotal = Number(totalTime.toFixed(2))
+      if (fixedTotal > 24) {
+        callback(new Error(`总时间(${fixedTotal}h)不能超过 24 小时`))
+      } else {
+        callback()
+      }
+    },
+    trigger: 'blur'
+  }
+}
+
+function handleRowValidate(key: string) {
+  if (!formRef.value) return
+
+  const propsToValidate = NON_PROD_FIELDS.map((field) => field.key)
+
+  if (key === 'otherNptTime') propsToValidate.push('otherNptReason')
+
+  formRef.value.validateField(propsToValidate)
+}
+
+const rules: FormRules = {
+  timeRange: [
+    { required: true, message: '请选择时间节点', trigger: ['blur', 'change'], type: 'array' }
+  ],
+  location: [{ required: true, message: '请输入施工地点', trigger: ['blur', 'change'] }],
+  responsiblePerson: [
+    { required: true, message: '请选择带班干部', trigger: ['blur', 'change'], type: 'array' }
+  ],
+  rdStatus: [{ required: true, message: '请选择施工状态', trigger: ['blur', 'change'] }],
+  dailyFuel: [{ required: true, message: '请输入当日油耗', trigger: ['blur', 'change'] }],
+  productionStatus: [{ required: true, message: '请输入生产状态', trigger: ['blur', 'change'] }],
+  nextPlan: [{ required: true, message: '请输入下计划', trigger: ['blur', 'change'] }]
+}
+
+interface UserOptions {
+  label: string
+  value: number
+  raw: any
+}
+
+const userOptions = ref<UserOptions[]>([])
+
+const deptName = ref('')
+
+async function loadDept() {
+  const res = await DeptApi.getDept(deptId)
+  deptName.value = res.name
+}
+
+async function loadUserOptions() {
+  const res = await UserApi.selectedDeptsEmployee({ deptIds: [deptId] })
+  userOptions.value = res.map((item: any) => ({
+    label: item.nickname,
+    value: item.id,
+    raw: item
+  }))
+}
+
+const loading = ref(false)
+
+async function load() {
+  loading.value = true
+
+  await loadUserOptions()
+
+  if (props.isview && props.id) {
+    const res = await IotRdDailyReportApi.getIotRdDailyReport(props.id)
+    deptName.value = res.deptName
+    form.value = {
+      deptId: res.deptId,
+      location: res.location,
+      responsiblePerson: res.responsiblePerson,
+      timeRange: [formatT(res.startTime), formatT(res.endTime)],
+      rdStatus: res.rdStatus,
+      dailyFuel: res.dailyFuel,
+      productionStatus: res.productionStatus,
+      nextPlan: res.nextPlan,
+      externalRental: res.externalRental,
+      malfunction: res.malfunction,
+      otherNptReason: res.otherNptReason,
+      ...NON_PROD_FIELDS.reduce((acc, field) => ({ ...acc, [field.key]: res[field.key] }), {})
+    }
+  } else {
+    await loadDept()
+  }
+
+  loading.value = false
+}
+
+const rdStatusOptions = getStrDictOptions(DICT_TYPE.PMS_PROJECT_RD_STATUS)
+
+function handleOpenForm() {
+  form.value = { ...original }
+  load()
+  emits('update:visible', true)
+}
+
+function handleCloseForm() {
+  emits('update:visible', false)
+}
+
+defineExpose({ handleOpenForm })
+
+const formLoading = ref(false)
+
+const message = useMessage()
+
+async function submitForm() {
+  if (!formRef.value) return
+
+  try {
+    formLoading.value = true
+    await formRef.value.validate()
+    const { timeRange, ...rest } = form.value
+    const data: any = { ...rest, startTime: timeRange?.[0], endTime: timeRange?.[1] }
+
+    if (props.id) {
+      data.id = props.id
+    }
+    await IotRdDailyReportApi.createIotRdDailyReport(data)
+
+    if (props.id) message.success('更新成功')
+    else message.success('新增成功')
+    handleCloseForm()
+    props.loadList()
+  } catch (error) {
+    console.log('error :>> ', error)
+  } finally {
+    formLoading.value = false
+  }
+}
+</script>
+
+<template>
+  <el-drawer
+    :model-value="visible"
+    @update:model-value="emits('update:visible', $event)"
+    header-class="mb-0!"
+    :with-header="false"
+    size="50%"
+    :close-on-press-escape="false"
+    :close-on-click-modal="false"
+  >
+    <div class="size-full flex flex-col gap-4">
+      <div class="flex justify-between items-center">
+        <div class="text-xl font-bold text-gray-900 leading-tight"> 新建日报 </div>
+        <el-button link @click="handleCloseForm">
+          <el-icon size="24"><Close /></el-icon>
+        </el-button>
+      </div>
+      <el-form
+        ref="formRef"
+        size="default"
+        label-position="top"
+        :model="form"
+        require-asterisk-position="right"
+        class="flex flex-col"
+        :rules="rules"
+        :loading="loading"
+        :disabled="props.isview !== 'create'"
+      >
+        <div class="grid grid-cols-2 gap-x-8">
+          <el-form-item label="施工队伍">{{ deptName }}</el-form-item>
+          <el-form-item label="施工地点" prop="location">
+            <el-input v-model="form.location" clearable placeholder="请输入施工地点" />
+          </el-form-item>
+          <el-form-item label="带班干部" prop="responsiblePerson">
+            <el-select
+              v-model="form.responsiblePerson"
+              multiple
+              :options="userOptions"
+              clearable
+              filterable
+              collapse-tags
+              collapse-tags-tooltip
+              :max-collapse-tags="5"
+              tag-type="primary"
+              placeholder="请选择带班干部"
+            />
+          </el-form-item>
+          <el-form-item label="时间节点" prop="timeRange">
+            <el-time-picker
+              v-model="form.timeRange"
+              is-range
+              range-separator="至"
+              start-placeholder="开始时间"
+              end-placeholder="结束时间"
+              placeholder="选择时间范围"
+              clearable
+              format="HH:mm"
+              value-format="HH:mm"
+            />
+          </el-form-item>
+          <el-form-item label="施工状态" prop="rdStatus">
+            <el-select
+              v-model="form.rdStatus"
+              :options="rdStatusOptions"
+              placeholder="请选择"
+              class="w-full"
+              clearable
+            />
+          </el-form-item>
+          <el-form-item label="当日油耗(L)" prop="dailyFuel">
+            <el-input-number
+              v-model="form.dailyFuel"
+              :min="0"
+              :controls="false"
+              align="left"
+              class="w-full!"
+              placeholder="请输入当日油耗"
+            >
+              <template #suffix>升(L)</template>
+            </el-input-number>
+          </el-form-item>
+          <el-form-item label="当日生产动态" prop="productionStatus">
+            <el-input
+              v-model="form.productionStatus"
+              type="textarea"
+              :autosize="{ minRows: 2 }"
+              resize="none"
+              show-word-limit
+              :maxlength="1000"
+              placeholder="请输入外组设备"
+            />
+          </el-form-item>
+          <el-form-item label="下步工作计划" prop="nextPlan">
+            <el-input
+              v-model="form.nextPlan"
+              type="textarea"
+              :autosize="{ minRows: 2 }"
+              resize="none"
+              show-word-limit
+              :maxlength="1000"
+              placeholder="请输入下步工作计划"
+            />
+          </el-form-item>
+
+          <el-form-item label="外租设备" prop="externalRental">
+            <el-input
+              v-model="form.externalRental"
+              type="textarea"
+              :autosize="{ minRows: 2 }"
+              resize="none"
+              show-word-limit
+              :maxlength="1000"
+              placeholder="请输入外组设备"
+            />
+          </el-form-item>
+          <el-form-item label="故障情况" prop="malfunction">
+            <el-input
+              v-model="form.malfunction"
+              type="textarea"
+              :autosize="{ minRows: 2 }"
+              show-word-limit
+              resize="none"
+              :maxlength="1000"
+              placeholder="请输入故障情况"
+            />
+          </el-form-item>
+        </div>
+        <el-divider class="m-0! mb-3! border-2! border-[var(--el-color-primary)]!" />
+        <h3 class="text-lg font-bold text-gray-800 mb-6 flex items-center gap-2">
+          <div class="w-1 h-4 bg-blue-600 rounded-full"></div>
+          非生产时间
+        </h3>
+        <div class="grid grid-cols-4 gap-x-8">
+          <el-form-item
+            v-for="field in NON_PROD_FIELDS"
+            :key="field.key"
+            :label="field.label"
+            :prop="field.key"
+            :rules="noProductionTimeRule()"
+          >
+            <el-input-number
+              v-model="form[field.key]"
+              :min="0"
+              :max="24"
+              :controls="false"
+              class="w-full!"
+              align="left"
+              @blur="handleRowValidate(field.key)"
+              :disabled="isview === 'detail'"
+            >
+              <template #suffix>小时(H)</template>
+            </el-input-number>
+          </el-form-item>
+
+          <el-form-item
+            class="col-span-4"
+            label="其他非生产原因"
+            prop="otherNptReason"
+            :rules="{
+              required: form.otherNptTime > 0,
+              message: '请填写原因',
+              trigger: ['blur', 'change']
+            }"
+          >
+            <el-input
+              v-model="form.otherNptReason"
+              type="textarea"
+              :autosize="{ minRows: 2 }"
+              resize="none"
+              show-word-limit
+              :maxlength="1000"
+              placeholder="当'其他非生产时间'大于0时必填"
+              :disabled="isview === 'detail'"
+            />
+          </el-form-item>
+        </div>
+      </el-form>
+    </div>
+    <template #footer>
+      <el-button
+        :disabled="isview === 'detail'"
+        size="default"
+        type="primary"
+        @click="submitForm"
+        :loading="formLoading"
+      >
+        确 定
+      </el-button>
+      <el-button size="default" @click="emits('update:visible', false)">取 消</el-button>
+    </template>
+  </el-drawer>
+</template>
+
+<style scoped>
+:deep(.el-form-item__label) {
+  font-weight: 500;
+}
+</style>

+ 37 - 11
src/views/pms/iotrddailyreport/fillDailyReport.vue

@@ -6,7 +6,7 @@ import { DICT_TYPE, getDictOptions } from '@/utils/dict'
 import { rangeShortcuts } from '@/utils/formatTime'
 import { useDebounceFn } from '@vueuse/core'
 import dayjs from 'dayjs'
-import rdForm from './rd-form.vue'
+import createRdForm from './create-rd-form.vue'
 
 defineOptions({ name: 'FillDailyReport' })
 
@@ -59,6 +59,8 @@ interface ListItem {
   responsiblePersonNames: string
   submitterNames: string
   nonProductFlag: boolean
+  projectId?: number
+  taskId?: number
 }
 
 const list = ref<ListItem[]>([])
@@ -123,15 +125,26 @@ watch(
 
 const visible = ref(false)
 
-// const formRef = ref()
+const formRef = ref()
 
-// function handleOpenForm(id: number, type: 'edit' | 'detail' | 'approval' | 'time') {
-//   if (formRef.value) {
-//     formRef.value.handleOpenForm(id, type)
-//   }
-// }
+const isview = ref<'create' | 'detail' | 'time'>('create')
+const viewId = ref<number>()
+
+function handleOpenCreateForm(id?: number, type?: any) {
+  if (formRef.value) {
+    if (id) {
+      viewId.value = id
+      isview.value = type
+    }
+    formRef.value.handleOpenForm()
+  }
+}
 
-function handleOpenForm(id: number, type: 'edit' | 'detail' | 'approval' | 'time') {
+function handleOpenForm(id: number, type: 'edit' | 'detail' | 'approval' | 'time', row?: ListItem) {
+  if (row && row.projectId === null && row.taskId === null) {
+    handleOpenCreateForm(id, type)
+    return
+  }
   router.push({
     name: 'FillDailyReportForm',
     query: { id: id, mode: type, backpath: route.path }
@@ -219,6 +232,13 @@ function realValue(type: any, value: string) {
         </el-form-item>
       </div>
       <el-form-item>
+        <el-button
+          type="primary"
+          @click="handleOpenCreateForm()"
+          v-hasPermi="['pms:iot-rd-daily-report:update']"
+        >
+          新增日报
+        </el-button>
         <el-button type="primary" @click="handleQuery()">
           <Icon icon="ep:search" class="mr-5px" /> 搜索
         </el-button>
@@ -262,7 +282,7 @@ function realValue(type: any, value: string) {
                   <el-button
                     link
                     type="success"
-                    @click="handleOpenForm(scope.row.id, 'detail')"
+                    @click="handleOpenForm(scope.row.id, 'detail', scope.row)"
                     v-hasPermi="['pms:iot-rd-daily-report:query']"
                   >
                     查看
@@ -274,7 +294,7 @@ function realValue(type: any, value: string) {
                         ? 'success'
                         : 'warning'
                     "
-                    @click="handleOpenForm(scope.row.id, 'time')"
+                    @click="handleOpenForm(scope.row.id, 'time', scope.row)"
                     v-hasPermi="['pms:iot-rd-daily-report:non-productive']"
                     v-if="scope.row.auditStatus === 20"
                   >
@@ -366,7 +386,13 @@ function realValue(type: any, value: string) {
       </div>
     </div>
   </div>
-  <rd-form ref="formRef" :load-list="loadList" v-model:visible="visible" />
+  <create-rd-form
+    :isview="isview"
+    :id="viewId"
+    ref="formRef"
+    :load-list="loadList"
+    v-model:visible="visible"
+  />
   <!-- <IotRdDailyReportForm ref="formRef" @success="loadList" /> -->
 </template>
 

+ 32 - 12
src/views/pms/iotrddailyreport/index.vue

@@ -7,7 +7,7 @@ import { formatT, rangeShortcuts } from '@/utils/formatTime'
 import { useDebounceFn } from '@vueuse/core'
 import dayjs from 'dayjs'
 import download from '@/utils/download'
-import rdForm from './rd-form.vue'
+import createRdForm from './create-rd-form.vue'
 
 defineOptions({ name: 'IotRdDailyReport' })
 
@@ -81,6 +81,8 @@ interface ListItem {
   contractName: string
   timeRange: string
   auditStatus: number
+  projectId: number
+  taskId: number
 }
 
 const list = ref<ListItem[]>([])
@@ -196,15 +198,27 @@ function realValue(type: any, value: string) {
 
 const visible = ref(false)
 
-// const formRef = ref()
+const formRef = ref()
 
-// function handleOpenForm(id: number, type: 'edit' | 'detail' | 'approval' | 'time') {
-//   if (formRef.value) {
-//     formRef.value.handleOpenForm(id, type)
-//   }
-// }
+const isview = ref<'create' | 'detail' | 'time'>('create')
+const viewId = ref<number>()
 
-function handleOpenForm(id: number, type: 'edit' | 'detail' | 'approval' | 'time') {
+function handleOpenCreateForm(id?: number, type?: any) {
+  if (formRef.value) {
+    if (id) {
+      viewId.value = id
+      isview.value = type
+    }
+    formRef.value.handleOpenForm()
+  }
+}
+
+function handleOpenForm(id: number, type: 'edit' | 'detail' | 'approval' | 'time', row?: ListItem) {
+  console.log('row', row)
+  if (row && row.projectId === null && row.taskId === null) {
+    handleOpenCreateForm(id, type)
+    return
+  }
   router.push({
     name: 'FillDailyReportForm',
     query: { id: id, mode: type, backpath: route.path }
@@ -357,7 +371,7 @@ onMounted(() => {
                                 {{ row.reportDetails[0].duration }} H
                               </div>
                             </div>
-
+                            <!--
                             <div class="flex items-center">
                               <div class="font-medium flex-shrink-0">施工详情:</div>
                               <el-tooltip
@@ -372,7 +386,7 @@ onMounted(() => {
                                   {{ row.reportDetails[0].constructionDetail || '-' }}
                                 </span>
                               </el-tooltip>
-                            </div>
+                            </div> -->
                           </div>
                         </el-badge>
                       </template>
@@ -492,7 +506,7 @@ onMounted(() => {
                   <el-button
                     link
                     type="success"
-                    @click="handleOpenForm(scope.row.id, 'detail')"
+                    @click="handleOpenForm(scope.row.id, 'detail', scope.row)"
                     v-hasPermi="['pms:iot-rd-daily-report:query']"
                   >
                     查看
@@ -529,7 +543,13 @@ onMounted(() => {
     </div>
   </div>
 
-  <rd-form ref="formRef" :load-list="loadList" v-model:visible="visible" />
+  <create-rd-form
+    :isview="isview"
+    :id="viewId"
+    ref="formRef"
+    :load-list="loadList"
+    v-model:visible="visible"
+  />
   <!-- <IotRdDailyReportForm ref="formRef" @success="loadList" /> -->
 </template>
 

+ 7 - 31
src/views/pms/iotrhdailyreport/rh-table.vue

@@ -195,6 +195,7 @@ function handleCurrentChange(val: number) {
                 </el-tag>
               </template>
             </zm-table-column>
+            <zm-table-column label="施工区域" prop="location" />
             <zm-table-column label="搬迁安装天数" prop="relocationDays" />
             <zm-table-column label="设计注气量(万方)" prop="designInjection" />
             <zm-table-column
@@ -241,44 +242,19 @@ function handleCurrentChange(val: number) {
             <zm-table-column prop="productionStatus" label="生产动态" />
             <zm-table-column prop="contractName" label="项目" />
             <zm-table-column label="井累计" v-if="isIndex">
-              <zm-table-column
-                prop="wellTotalGasInjection"
-                label="注气量(万方)"
-                cover-formatter
-                :real-value="unitformatter"
-              />
+              <zm-table-column prop="wellTotalGasInjection" label="注气量(万方)" />
               <zm-table-column prop="wellTotalWaterInjection" label="注水量(方)" />
-              <zm-table-column
-                prop="wellTotalPower"
-                label="用电量(MWh)"
-                cover-formatter
-                :real-value="unitformatter"
-              />
+              <zm-table-column prop="wellTotalPower" label="用电量(MWh)" />
               <zm-table-column prop="wellTotalFuel" label="油耗(L)" />
             </zm-table-column>
             <zm-table-column label="年累计" v-if="isIndex">
-              <zm-table-column
-                prop="yearTotalGasInjection"
-                label="注气量(万方)"
-                cover-formatter
-                :real-value="unitformatter"
-              />
+              <zm-table-column prop="yearTotalGasInjection" label="注气量(万方)" />
               <zm-table-column prop="yearTotalWaterInjection" label="注水量(方)" />
-              <zm-table-column
-                prop="yearTotalPower"
-                label="用电量(MWh)"
-                cover-formatter
-                :real-value="unitformatter"
-              />
-              <zm-table-column prop="wellTotalFuel" label="油耗(L)" />
+              <zm-table-column prop="yearTotalPower" label="用电量(MWh)" />
+              <zm-table-column prop="yearTotalFuel" label="油耗(L)" />
             </zm-table-column>
             <zm-table-column label="累计" v-if="!isIndex">
-              <zm-table-column
-                prop="totalGasInjection"
-                label="注气量(万方)"
-                cover-formatter
-                :real-value="unitformatter"
-              />
+              <zm-table-column prop="totalGasInjection" label="注气量(万方)" />
               <zm-table-column prop="totalWaterInjection" label="注水量(方)" />
               <zm-table-column prop="cumulativeCompletion" label="完工井次" />
             </zm-table-column>

+ 17 - 1
src/views/pms/iotrydailyreport/ry-form.vue

@@ -97,6 +97,8 @@ interface FormOriginal {
   opinion: string
 
   auditStatus: number
+
+  nextPlan: string
 }
 
 type Form = Partial<FormOriginal>
@@ -143,7 +145,8 @@ const FORM_KEYS: (keyof FormOriginal)[] = [
   'winterBreakTime',
   'otherNptTime',
   'otherNptReason',
-  'auditStatus'
+  'auditStatus',
+  'nextPlan'
 ]
 
 const formRef = ref<FormInstance>()
@@ -898,6 +901,19 @@ const inputCurrentDepth = useDebounceFn(function inputCurrentDepth() {
           />
         </el-form-item>
 
+        <el-form-item class="col-span-2" label="下步计划" prop="nextPlan">
+          <el-input
+            v-model="form.nextPlan"
+            type="textarea"
+            :autosize="{ minRows: 2 }"
+            show-word-limit
+            resize="none"
+            :maxlength="1000"
+            placeholder="请输入当日施工简报"
+            :disabled="!isApproval"
+          />
+        </el-form-item>
+
         <!-- 时间信息 -->
         <div class="col-span-2 flex items-center gap-2 mt-4">
           <div class="bg-[var(--el-color-primary)] w-1 h-5 rounded-full"></div>

+ 4 - 2
src/views/pms/iotrydailyreport/ry-table.vue

@@ -230,6 +230,7 @@ function handleCurrentChange(val: number) {
               <zm-table-column label="月" prop="monthlyFootage" />
               <zm-table-column label="年" prop="annualFootage" />
             </zm-table-column>
+            <zm-table-column label="施工区域" prop="location" />
             <zm-table-column label="总施工井数" prop="totalConstructionWells" />
             <zm-table-column label="完工井数" prop="completedWells" />
             <zm-table-column label="泥浆性能">
@@ -282,7 +283,7 @@ function handleCurrentChange(val: number) {
                             </div>
                           </div>
 
-                          <div class="flex items-center">
+                          <!-- <div class="flex items-center">
                             <div class="font-medium flex-shrink-0">结束井深(m):</div>
                             <span
                               class="font-medium truncate group-hover:text-blue-600 transition-colors"
@@ -320,7 +321,7 @@ function handleCurrentChange(val: number) {
                                 {{ row.reportDetails[0].constructionDetail || '-' }}
                               </span>
                             </el-tooltip>
-                          </div>
+                          </div> -->
                         </div>
                       </el-badge>
                     </template>
@@ -379,6 +380,7 @@ function handleCurrentChange(val: number) {
               </template>
             </zm-table-column>
             <zm-table-column prop="constructionBrief" label="当日施工简报" />
+            <zm-table-column prop="nextPlan" label="下步计划" />
             <zm-table-column prop="contractName" label="项目" />
             <zm-table-column prop="drillingWorkingTime" label="进尺工作时间(H)" />
             <zm-table-column prop="otherProductionTime" label="其它生产时间(H)" />

+ 3 - 2
src/views/pms/iotrydailyreport/ry-xj-table.vue

@@ -247,6 +247,7 @@ function handleCurrentChange(val: number) {
                 </el-tag>
               </template>
             </zm-table-column>
+            <zm-table-column label="施工区域" prop="location" />
             <zm-table-column label="总施工井数" prop="totalConstructionWells" />
             <zm-table-column label="完工井数" prop="completedWells" />
             <zm-table-column
@@ -351,7 +352,7 @@ function handleCurrentChange(val: number) {
                             </span>
                           </div> -->
 
-                          <div class="flex items-center">
+                          <!-- <div class="flex items-center">
                             <div class="font-medium flex-shrink-0">工况:</div>
                             <el-tooltip
                               effect="dark"
@@ -381,7 +382,7 @@ function handleCurrentChange(val: number) {
                                 {{ row.reportDetails[0].constructionDetail || '-' }}
                               </span>
                             </el-tooltip>
-                          </div>
+                          </div> -->
                         </div>
                       </el-badge>
                     </template>