Просмотр исходного кода

🦄 refactor(日报填报审批): 调整非生产时间

Zimo 4 дней назад
Родитель
Сommit
9ab99f0532

+ 20 - 379
src/views/pms/iotrhdailyreport/approval.vue

@@ -3,11 +3,10 @@ import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
 import { rangeShortcuts } from '@/utils/formatTime'
 import { useDebounceFn } from '@vueuse/core'
 import dayjs from 'dayjs'
-import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { DICT_TYPE } from '@/utils/dict'
 import { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults'
-import { FormInstance, FormRules } from 'element-plus'
-import Form from '@/components/Form/src/Form.vue'
 import { useUserStore } from '@/store/modules/user'
+import rhForm from './rh-form.vue'
 
 interface List {
   createTime: number // 日期
@@ -272,18 +271,18 @@ const calculateColumnWidths = (colums: Column[]) => {
   }
 }
 
-function checkTimeSumEquals24(row: List) {
-  // 获取三个字段的值,转换为数字,如果为空则视为0
-  const gasTime = row.dailyInjectGasTime || 0
-  const waterTime = row.dailyInjectWaterTime || 0
-  const nonProdTime = row.nonProductionTime || 0
+// function checkTimeSumEquals24(row: List) {
+//   // 获取三个字段的值,转换为数字,如果为空则视为0
+//   const gasTime = row.dailyInjectGasTime || 0
+//   const waterTime = row.dailyInjectWaterTime || 0
+//   const nonProdTime = row.nonProductionTime || 0
 
-  // 计算总和
-  const sum = gasTime + waterTime + nonProdTime
+//   // 计算总和
+//   const sum = gasTime + waterTime + nonProdTime
 
-  // 返回是否等于24(允许一定的浮点数误差)
-  return Math.abs(sum - 24) < 0.01 // 使用0.01作为误差范围
-}
+//   // 返回是否等于24(允许一定的浮点数误差)
+//   return Math.abs(sum - 24) < 0.01 // 使用0.01作为误差范围
+// }
 
 function cellStyle(data: {
   row: List
@@ -403,181 +402,23 @@ watch(
   { immediate: true }
 )
 
-const FORM_KEYS = [
-  'id',
-  'deptName',
-  'contractName',
-  'taskName',
-  'dailyGasInjection',
-  'dailyWaterInjection',
-  'dailyInjectGasTime',
-  'dailyInjectWaterTime',
-  'nonProductionTime',
-  'nptReason',
-  'productionStatus',
-  'remark',
-  'relocationDays',
-  'capacity',
-  'createTime',
-  'deptId',
-  'projectId',
-  'taskId',
-  'auditStatus',
-  'opinion'
-] as const
-
-type FormKey = (typeof FORM_KEYS)[number]
-type Form = Partial<Pick<List, FormKey>>
-
-const dialogVisible = ref(false)
-const formRef = ref<FormInstance>()
-const formLoading = ref(false)
-const message = useMessage()
-
-const initFormData = (): Form => ({
-  dailyGasInjection: 0,
-  dailyWaterInjection: 0,
-  dailyInjectGasTime: 0,
-  dailyInjectWaterTime: 0,
-  nonProductionTime: 0,
-  relocationDays: 0,
-  capacity: 0
-})
-
-const form = ref<Form>(initFormData())
-
-async function loadDetail(id: number) {
-  try {
-    const res = await IotRhDailyReportApi.getIotRhDailyReport(id)
-    FORM_KEYS.forEach((key) => {
-      form.value[key] = res[key] ?? form.value[key]
-    })
-    form.value.id = id
+const visible = ref(false)
 
-    if (res.auditStatus !== 10) {
-      formType.value = 'readonly'
-    }
+const formRef = ref()
 
-    // if (!form.value.capacity) {
-    //   message.error('请维护增压机产能')
-    // }
-  } finally {
+function handleOpenForm(id: number, type: 'edit' | 'readonly') {
+  if (formRef.value) {
+    formRef.value.handleOpenForm(id, type)
   }
 }
 
-const formType = ref<'approval' | 'readonly'>('approval')
-
-function handleOpenForm(id: number, type: 'approval' | 'readonly') {
-  form.value = initFormData()
-  formRef.value?.resetFields()
-
-  formType.value = type
-
-  dialogVisible.value = true
-  loadDetail(id).then(() => {
-    formRef.value?.validate()
-  })
-}
-
 const route = useRoute()
 
 onMounted(() => {
   if (Object.keys(route.query).length > 0) {
-    handleOpenForm(Number(route.query.id), 'approval')
-  }
-})
-
-const transitTime = computed(() => {
-  const cap = form.value.capacity
-  const gas = form.value.dailyGasInjection ?? 0
-
-  if (!cap) return { original: 0, value: '0%' }
-
-  const original = gas / cap
-  return { original, value: (original * 100).toFixed(2) + '%' }
-})
-
-const sumTimes = () => {
-  const { dailyInjectGasTime = 0, dailyInjectWaterTime = 0, nonProductionTime = 0 } = form.value
-  return parseFloat((dailyInjectGasTime + dailyInjectWaterTime + nonProductionTime).toFixed(2))
-}
-
-const validateTotalTime = (_rule: any, _value: any, callback: any) => {
-  const total = sumTimes()
-  if (total !== 24) {
-    callback(new Error(`当前合计 ${total} 小时,三项时间之和必须等于 24`))
-  } else {
-    callback()
-  }
-}
-
-const validateNptReason = (_rule: any, value: any, callback: any) => {
-  if ((form.value.nonProductionTime || 0) > 0 && !value) {
-    callback(new Error('非生产时间大于 0 时,必须选择原因'))
-  } else {
-    callback()
+    handleOpenForm(Number(route.query.id), 'edit')
   }
-}
-
-const timeRuleItem = [
-  { required: true, message: '请输入时间', trigger: 'blur' },
-  { validator: validateTotalTime, trigger: 'blur' }
-]
-
-const rules = reactive<FormRules>({
-  dailyGasInjection: [{ required: true, message: '请输入当日注气量', trigger: 'blur' }],
-  dailyWaterInjection: [{ required: true, message: '请输入当日注水量', trigger: 'blur' }],
-  productionStatus: [{ required: true, message: '请输入生产动态', trigger: 'blur' }],
-
-  // 复用规则
-  // dailyInjectGasTime: timeRuleItem,
-  // dailyInjectWaterTime: timeRuleItem,
-  // nonProductionTime: timeRuleItem,
-
-  nptReason: [{ validator: validateNptReason, trigger: ['change', 'blur'] }]
 })
-
-watch(
-  [
-    () => form.value.dailyInjectGasTime,
-    () => form.value.dailyInjectWaterTime,
-    () => form.value.nonProductionTime
-  ],
-  () => {
-    nextTick(() => {
-      formRef.value?.validateField('nptReason')
-      if (sumTimes() === 24) {
-        formRef.value?.clearValidate([
-          'dailyInjectGasTime',
-          'dailyInjectWaterTime',
-          'nonProductionTime'
-        ])
-      }
-    })
-  }
-)
-
-const submitForm = async (auditStatus: 20 | 30) => {
-  if (!formRef.value) return
-
-  try {
-    // await formRef.value.validate()
-    formLoading.value = true
-    const { opinion, id } = form.value
-
-    const data = { id: id, auditStatus, opinion } as any
-
-    await IotRhDailyReportApi.approvalIotRhDailyReport(data)
-    message.success(auditStatus === 20 ? '通过成功' : '拒绝成功')
-    dialogVisible.value = false
-
-    loadList()
-  } catch (error) {
-    console.warn('表单校验未通过或提交出错')
-  } finally {
-    formLoading.value = false
-  }
-}
 </script>
 
 <template>
@@ -662,7 +503,7 @@ const submitForm = async (auditStatus: 20 | 30) => {
                         v-show="row.auditStatus === 10"
                         link
                         type="primary"
-                        @click="handleOpenForm(row.id, 'approval')"
+                        @click="handleOpenForm(row.id, 'edit')"
                         v-hasPermi="['pms:iot-rh-daily-report:update']"
                       >
                         审批
@@ -690,193 +531,7 @@ const submitForm = async (auditStatus: 20 | 30) => {
         </div>
       </div>
     </div>
-    <Dialog title="编辑" v-model="dialogVisible">
-      <el-form
-        ref="formRef"
-        label-position="top"
-        size="default"
-        :rules="rules"
-        :model="form"
-        v-loading="formLoading"
-        require-asterisk-position="right"
-      >
-        <div class="flex flex-col gap-3 text-sm">
-          <div
-            class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
-          >
-            <div class="flex flex-col gap-2.5">
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">运行时效:</span>
-                  当日注气量 / 产能
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
-                >
-                  >120% 红色预警
-                </span>
-              </div>
-              <!-- <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
-                  注气 + 注水 + 非生产 = 24H
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
-                >
-                  ≠24H 橙色预警
-                </span>
-              </div> -->
-            </div>
-          </div>
-          <!-- <div
-          v-if="form.opinion"
-          class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
-        >
-          <Icon
-            icon="ep:warning-filled"
-            class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
-          />
-          <div class="flex flex-col">
-            <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500"> 审核意见 </h4>
-            <p class="leading-relaxed text-gray-600 dark:text-gray-400">
-              {{ form.opinion }}
-            </p>
-          </div>
-        </div> -->
-        </div>
-        <div class="grid grid-cols-2 gap-4 mt-5">
-          <el-form-item label="施工队伍" prop="deptName">
-            <el-input v-model="form.deptName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="项目" prop="contractName">
-            <el-input v-model="form.contractName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="任务" prop="taskName">
-            <el-input v-model="form.taskName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="搬迁安装天数(D)" prop="relocationDays">
-            <el-input v-model="form.relocationDays" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="运行时效" prop="transitTime">
-            <el-input
-              :model-value="transitTime.value"
-              placeholder="运行时效"
-              disabled
-              :class="{ 'warning-input': transitTime.original > 1.2 }"
-              id="transitTimeInput"
-            />
-          </el-form-item>
-          <el-form-item label="当日注气量(方)" prop="dailyGasInjection">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyGasInjection"
-              placeholder="请输入当日注气量(方)"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="当日注水量(方)" prop="dailyWaterInjection">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyWaterInjection"
-              placeholder="请输入当日注水量(方)"
-              disabled
-            />
-          </el-form-item>
-          <!-- :class="{ 'orange-input': sumTimes() !== 24 }" -->
-          <el-form-item label="当日注气时间(H)" prop="dailyInjectGasTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyInjectGasTime"
-              placeholder="请输入当日注气时间(H)"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="当日注水时间(H)" prop="dailyInjectWaterTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyInjectWaterTime"
-              placeholder="当日注水时间(H)"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="非生产时间(H)" prop="nonProductionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.nonProductionTime"
-              placeholder="非生产时间(H)"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="非生产时间原因" prop="nptReason">
-            <el-select v-model="form.nptReason" placeholder="请选择" disabled clearable>
-              <el-option
-                v-for="(dict, index) of getStrDictOptions(DICT_TYPE.PMS_PROJECT_NPT_REASON)"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <div class="grid grid-cols-1 gap-4 mt-5">
-            <el-form-item label="生产动态" prop="productionStatus">
-              <el-input
-                v-model="form.productionStatus"
-                placeholder="请输入生产动态"
-                type="textarea"
-                autosize
-                :max-length="1000"
-                disabled
-              />
-            </el-form-item>
-            <el-form-item label="备注" prop="remark">
-              <el-input
-                v-model="form.remark"
-                placeholder="请输入备注"
-                :max-length="1000"
-                type="textarea"
-                autosize
-                disabled
-              />
-            </el-form-item>
-          </div>
-        </div>
-        <el-form-item class="mt-4" label="审批意见" prop="opinion">
-          <el-input
-            v-model="form.opinion"
-            placeholder="请输入审批意见"
-            :max-length="1000"
-            type="textarea"
-            autosize
-            :disabled="formType === 'readonly'"
-          />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button
-          size="default"
-          @click="submitForm(20)"
-          type="primary"
-          :disabled="formLoading || formType === 'readonly'"
-        >
-          审批通过
-        </el-button>
-        <el-button
-          size="default"
-          @click="submitForm(30)"
-          type="danger"
-          :disabled="formLoading || formType === 'readonly'"
-        >
-          审批拒绝
-        </el-button>
-        <el-button size="default" @click="dialogVisible = false">取 消</el-button>
-      </template>
-    </Dialog>
+    <rh-form v-model:visible="visible" type="approval" ref="formRef" :load-list="loadList" />
   </div>
 </template>
 
@@ -900,20 +555,6 @@ const submitForm = async (auditStatus: 20 | 30) => {
   }
 }
 
-:deep(.warning-input) {
-  .el-input__inner {
-    color: red !important;
-    -webkit-text-fill-color: red !important;
-  }
-}
-
-:deep(.orange-input) {
-  .el-input__inner {
-    color: orange !important;
-    -webkit-text-fill-color: orange !important;
-  }
-}
-
 :deep(.el-input-number__decrease) {
   display: none !important;
 }

+ 10 - 337
src/views/pms/iotrhdailyreport/fill.vue

@@ -271,18 +271,18 @@ const calculateColumnWidths = (colums: Column[]) => {
   }
 }
 
-function checkTimeSumEquals24(row: List) {
-  // 获取三个字段的值,转换为数字,如果为空则视为0
-  const gasTime = row.dailyInjectGasTime || 0
-  const waterTime = row.dailyInjectWaterTime || 0
-  const nonProdTime = row.nonProductionTime || 0
+// function checkTimeSumEquals24(row: List) {
+//   // 获取三个字段的值,转换为数字,如果为空则视为0
+//   const gasTime = row.dailyInjectGasTime || 0
+//   const waterTime = row.dailyInjectWaterTime || 0
+//   const nonProdTime = row.nonProductionTime || 0
 
-  // 计算总和
-  const sum = gasTime + waterTime + nonProdTime
+//   // 计算总和
+//   const sum = gasTime + waterTime + nonProdTime
 
-  // 返回是否等于24(允许一定的浮点数误差)
-  return Math.abs(sum - 24) < 0.01 // 使用0.01作为误差范围
-}
+//   // 返回是否等于24(允许一定的浮点数误差)
+//   return Math.abs(sum - 24) < 0.01 // 使用0.01作为误差范围
+// }
 
 function cellStyle(data: {
   row: List
@@ -402,69 +402,6 @@ watch(
   { immediate: true }
 )
 
-// const FORM_KEYS = [
-//   'id',
-//   'deptId',
-//   'projectId',
-//   'taskId',
-//   'deptName',
-//   'contractName',
-//   'taskName',
-//   'dailyGasInjection',
-//   'dailyWaterInjection',
-//   'dailyInjectGasTime',
-//   'dailyInjectWaterTime',
-//   'nonProductionTime',
-//   'nptReason',
-//   'productionStatus',
-//   'remark',
-//   'relocationDays',
-//   'capacity',
-//   'createTime',
-//   'opinion'
-// ] as const
-
-// type FormKey = (typeof FORM_KEYS)[number]
-// type Form = Partial<Pick<List, FormKey>>
-
-// const dialogVisible = ref(false)
-// const formRef = ref<FormInstance>()
-// const formLoading = ref(false)
-// const message = useMessage()
-
-// const initFormData = (): Form => ({
-//   dailyGasInjection: 0,
-//   dailyWaterInjection: 0,
-//   dailyInjectGasTime: 0,
-//   dailyInjectWaterTime: 0,
-//   nonProductionTime: 0,
-//   relocationDays: 0,
-//   capacity: 0
-// })
-
-// const form = ref<Form>(initFormData())
-
-// async function loadDetail(id: number) {
-//   try {
-//     const res = await IotRhDailyReportApi.getIotRhDailyReport(id)
-//     FORM_KEYS.forEach((key) => {
-//       form.value[key] = res[key] ?? form.value[key]
-//     })
-//     form.value.id = id
-
-//     if (res.status !== 0) {
-//       formType.value = 'readonly'
-//     }
-
-//     if (!form.value.capacity) {
-//       message.error('请维护增压机产能')
-//     }
-//   } finally {
-//   }
-// }
-
-// const formType = ref<'edit' | 'readonly'>('edit')
-
 const visible = ref(false)
 
 const formRef = ref()
@@ -482,99 +419,6 @@ onMounted(() => {
     handleOpenForm(Number(route.query.id), 'edit')
   }
 })
-
-// const transitTime = computed(() => {
-//   const cap = form.value.capacity
-//   const gas = form.value.dailyGasInjection ?? 0
-
-//   if (!cap) return { original: 0, value: '0%' }
-
-//   const original = gas / cap
-//   return { original, value: (original * 100).toFixed(2) + '%' }
-// })
-
-// const sumTimes = () => {
-//   const { dailyInjectGasTime = 0, dailyInjectWaterTime = 0, nonProductionTime = 0 } = form.value
-//   return parseFloat((dailyInjectGasTime + dailyInjectWaterTime + nonProductionTime).toFixed(2))
-// }
-
-// const validateTotalTime = (_rule: any, _value: any, callback: any) => {
-//   const total = sumTimes()
-//   if (total !== 24) {
-//     callback(new Error(`当前合计 ${total} 小时,三项时间之和必须等于 24`))
-//   } else {
-//     callback()
-//   }
-// }
-
-// const validateNptReason = (_rule: any, value: any, callback: any) => {
-//   if ((form.value.nonProductionTime || 0) > 0 && !value) {
-//     callback(new Error('非生产时间大于 0 时,必须选择原因'))
-//   } else {
-//     callback()
-//   }
-// }
-
-// const timeRuleItem = [
-//   { required: true, message: '请输入时间', trigger: 'blur' },
-//   { validator: validateTotalTime, trigger: 'blur' }
-// ]
-
-// const rules = reactive<FormRules>({
-//   dailyGasInjection: [{ required: true, message: '请输入当日注气量', trigger: ['change', 'blur'] }],
-//   dailyWaterInjection: [
-//     { required: true, message: '请输入当日注水量', trigger: ['change', 'blur'] }
-//   ],
-//   productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['change', 'blur'] }],
-
-//   // 复用规则
-//   // dailyInjectGasTime: timeRuleItem,
-//   // dailyInjectWaterTime: timeRuleItem,
-//   // nonProductionTime: timeRuleItem,
-
-//   nptReason: [{ validator: validateNptReason, trigger: ['change', 'blur'] }]
-// })
-
-// watch(
-//   [
-//     () => form.value.dailyInjectGasTime,
-//     () => form.value.dailyInjectWaterTime,
-//     () => form.value.nonProductionTime
-//   ],
-//   () => {
-//     nextTick(() => {
-//       formRef.value?.validateField('nptReason')
-//       if (sumTimes() === 24) {
-//         formRef.value?.clearValidate([
-//           'dailyInjectGasTime',
-//           'dailyInjectWaterTime',
-//           'nonProductionTime'
-//         ])
-//       }
-//     })
-//   }
-// )
-
-// const { t } = useI18n()
-
-// const submitForm = async () => {
-//   if (!formRef.value) return
-
-//   try {
-//     await formRef.value.validate()
-//     formLoading.value = true
-//     const { createTime, ...other } = form.value
-//     const data = { ...other, fillOrderCreateTime: createTime } as any
-//     await IotRhDailyReportApi.createIotRhDailyReport(data)
-//     message.success(t('common.updateSuccess'))
-//     dialogVisible.value = false
-//     loadList()
-//   } catch (error) {
-//     console.warn('表单校验未通过或提交出错')
-//   } finally {
-//     formLoading.value = false
-//   }
-// }
 </script>
 
 <template>
@@ -688,163 +532,6 @@ onMounted(() => {
       </div>
     </div>
     <rh-form v-model:visible="visible" type="edit" ref="formRef" :load-list="loadList" />
-    <!-- <Dialog title="编辑">
-      <el-form
-        ref="formRef"
-        label-position="top"
-        size="default"
-        :rules="rules"
-        :model="form"
-        v-loading="formLoading"
-        require-asterisk-position="right"
-        :disabled="formType === 'readonly'"
-      >
-        <div class="flex flex-col gap-3 text-sm">
-          <div
-            class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
-          >
-            <div class="flex flex-col gap-2.5">
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">运行时效:</span>
-                  当日注气量 / 产能
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
-                >
-                  >120% 红色预警
-                </span>
-              </div>
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
-                  注气 + 注水 + 非生产 = 24H
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
-                >
-                  ≠24H 橙色预警
-                </span>
-              </div>
-            </div>
-          </div>
-          <div
-            v-if="form.opinion"
-            class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
-          >
-            <Icon
-              icon="ep:warning-filled"
-              class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
-            />
-            <div class="flex flex-col">
-              <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500"> 审核意见 </h4>
-              <p class="leading-relaxed text-gray-600 dark:text-gray-400">
-                {{ form.opinion }}
-              </p>
-            </div>
-          </div>
-        </div>
-        <div class="grid grid-cols-2 gap-4 mt-5">
-          <el-form-item label="施工队伍" prop="deptName">
-            <el-input v-model="form.deptName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="项目" prop="contractName">
-            <el-input v-model="form.contractName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="任务" prop="taskName">
-            <el-input v-model="form.taskName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="搬迁安装天数(D)" prop="relocationDays">
-            <el-input v-model="form.relocationDays" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="运行时效" prop="transitTime">
-            <el-input
-              :model-value="transitTime.value"
-              placeholder="运行时效"
-              disabled
-              :class="{ 'warning-input': transitTime.original > 1.2 }"
-              id="transitTimeInput"
-            />
-          </el-form-item>
-          <el-form-item label="当日注气量(方)" prop="dailyGasInjection">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyGasInjection"
-              placeholder="请输入当日注气量(方)"
-            />
-          </el-form-item>
-          <el-form-item label="当日注水量(方)" prop="dailyWaterInjection">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyWaterInjection"
-              placeholder="请输入当日注水量(方)"
-            />
-          </el-form-item>
-          <el-form-item label="当日注气时间(H)" prop="dailyInjectGasTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyInjectGasTime"
-              placeholder="请输入当日注气时间(H)"
-            />
-          </el-form-item>
-          <el-form-item label="当日注水时间(H)" prop="dailyInjectWaterTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyInjectWaterTime"
-              placeholder="当日注水时间(H)"
-            />
-          </el-form-item>
-          <el-form-item label="非生产时间(H)" prop="nonProductionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.nonProductionTime"
-              placeholder="非生产时间(H)"
-            />
-          </el-form-item>
-          <el-form-item label="非生产时间原因" prop="nptReason">
-            <el-select v-model="form.nptReason" placeholder="请选择" clearable>
-              <el-option
-                v-for="(dict, index) of getStrDictOptions(DICT_TYPE.PMS_PROJECT_NPT_REASON)"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-        </div>
-        <div class="grid grid-cols-1 gap-4 mt-5">
-          <el-form-item label="生产动态" prop="productionStatus">
-            <el-input
-              v-model="form.productionStatus"
-              placeholder="请输入生产动态"
-              type="textarea"
-              autosize
-              :max-length="1000"
-            />
-          </el-form-item>
-          <el-form-item label="备注" prop="remark">
-            <el-input
-              v-model="form.remark"
-              placeholder="请输入备注"
-              :max-length="1000"
-              type="textarea"
-              autosize
-            />
-          </el-form-item>
-        </div>
-      </el-form>
-      <template #footer>
-        <el-button size="default" @click="submitForm" type="primary" :disabled="formLoading">
-          确 定
-        </el-button>
-        <el-button size="default" @click="dialogVisible = false">取 消</el-button>
-      </template>
-    </Dialog> -->
   </div>
 </template>
 
@@ -868,20 +555,6 @@ onMounted(() => {
   }
 }
 
-:deep(.warning-input) {
-  .el-input__inner {
-    color: red !important;
-    -webkit-text-fill-color: red !important;
-  }
-}
-
-:deep(.orange-input) {
-  .el-input__inner {
-    color: orange !important;
-    -webkit-text-fill-color: orange !important;
-  }
-}
-
 :deep(.el-input-number) {
   width: 100%;
 }

+ 0 - 524
src/views/pms/iotrhdailyreport/rh-form copy.vue

@@ -1,524 +0,0 @@
-<script lang="ts" setup generic="T">
-import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
-import { FormInstance, FormRules } from 'element-plus'
-import { computed, reactive, ref, watch, nextTick } from 'vue'
-
-interface Props {
-  visible: boolean
-  type?: 'edit' | 'approval' | 'readonly'
-  loadList: () => void
-}
-
-const props = withDefaults(defineProps<Props>(), {
-  type: 'edit'
-})
-
-const emits = defineEmits(['update:visible'])
-
-// 1. 定义非生产时间字段映射,方便循环和计算
-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: '待命' }
-] as const
-
-interface FormOriginal {
-  id: number
-  deptName: string
-  contractName: string
-  taskName: string
-  dailyGasInjection: number
-  dailyWaterInjection: number
-  dailyInjectGasTime: number
-  dailyInjectWaterTime: number
-  // 新增非生产时间字段
-  repairTime: number
-  selfStopTime: number
-  accidentTime: number
-  complexityTime: number
-  rectificationTime: number
-  waitingStopTime: number
-  partyaDesign: number
-  partyaPrepare: number
-  partyaResource: number
-  relocationTime: number
-  winterBreakTime: number
-  otherNptReason: string // 其他非生产时间原因说明
-
-  productionStatus: string
-  remark: string
-  relocationDays: number
-  capacity: number
-  createTime: number
-  opinion: string
-}
-
-type Form = Partial<FormOriginal>
-
-const formRef = ref<FormInstance>()
-const loading = ref(false)
-const formLoading = ref(false)
-const formType = ref<'edit' | 'readonly'>('edit')
-const message = useMessage()
-const { t } = useI18n()
-
-// 初始化表单数据,包含新字段默认为0
-const initFormData = (): Form => {
-  const base: Form = {
-    dailyGasInjection: 0,
-    dailyWaterInjection: 0,
-    dailyInjectGasTime: 0,
-    dailyInjectWaterTime: 0,
-    relocationDays: 0,
-    capacity: 0,
-    otherNptReason: '',
-    opinion: ''
-  }
-  // 初始化所有非生产时间为0
-  NON_PROD_FIELDS.forEach((field) => {
-    base[field.key as keyof FormOriginal] = 0
-  })
-  return base
-}
-
-const form = ref<Form>(initFormData())
-
-// 计算属性:判断当前操作模式
-const isApproval = computed(() => props.type === 'approval')
-const isEdit = computed(() => props.type === 'edit')
-
-// 计算属性:主业务字段是否禁用
-// 规则:如果是 'readonly' 模式 或者 处于 'approval' (审批) 模式下,主业务数据不可改
-const isMainFieldDisabled = computed(() => {
-  return formType.value === 'readonly' || isApproval.value
-})
-
-async function loadDetail(id: number) {
-  loading.value = true
-  try {
-    const res = await IotRhDailyReportApi.getIotRhDailyReport(id)
-    // 简单的对象合并
-    form.value = { ...initFormData(), ...res }
-
-    // 逻辑控制:如果状态不是初始草稿,编辑模式下也转为只读(或者根据具体业务需求)
-    // 这里假设 status != 0 时,edit 模式下只能看
-    if (props.type === 'edit' && res.status !== 0) {
-      formType.value = 'readonly'
-    }
-    // 审批模式下,如果已经审批过了(假设10是待审批),则只读
-    if (props.type === 'approval' && res.auditStatus !== 10) {
-      formType.value = 'readonly'
-    }
-
-    if (!form.value.capacity) {
-      message.error('请维护增压机产能')
-    }
-  } finally {
-    loading.value = false
-  }
-}
-
-function handleOpenForm(id: number, type: 'edit' | 'readonly') {
-  form.value = initFormData()
-  formRef.value?.resetFields()
-  formType.value = type
-
-  emits('update:visible', true)
-  loadDetail(id).then(() => {
-    // 加载完成后清除校验,避免数字0触发校验红字
-    nextTick(() => formRef.value?.clearValidate())
-  })
-}
-
-defineExpose({ handleOpenForm })
-
-// --- 运行时效计算 ---
-const transitTime = computed(() => {
-  const cap = form.value.capacity
-  const gas = form.value.dailyGasInjection ?? 0
-  if (!cap) return { original: 0, value: '0%' }
-  const original = gas / cap
-  return { original, value: (original * 100).toFixed(2) + '%' }
-})
-
-// --- 时间校验逻辑 ---
-
-// 计算所有非生产时间之和
-const sumNonProdTimes = () => {
-  let sum = 0
-  NON_PROD_FIELDS.forEach((field) => {
-    sum += (form.value[field.key as keyof FormOriginal] as number) || 0
-  })
-  return sum
-}
-
-// 核心校验规则
-const validateTotalTime = (_rule: any, _value: any, callback: any) => {
-  const gasTime = form.value.dailyInjectGasTime || 0
-  const waterTime = form.value.dailyInjectWaterTime || 0
-  const nonProdSum = sumNonProdTimes()
-
-  let total = 0
-  let msg = ''
-
-  // 规则:
-  // (如果注气时间>0) 注气时间 + 所有非生产时间累计 = 24
-  // (如果注气时间=0) 注水时间 + 所有非生产时间累计 = 24
-  if (gasTime > 0) {
-    total = parseFloat((gasTime + nonProdSum).toFixed(2))
-    msg = `注气时间(${gasTime}) + 非生产时间合计(${nonProdSum}) 必须等于 24,当前合计: ${total}`
-  } else {
-    total = parseFloat((waterTime + nonProdSum).toFixed(2))
-    msg = `注水时间(${waterTime}) + 非生产时间合计(${nonProdSum}) 必须等于 24,当前合计: ${total}`
-  }
-
-  if (Math.abs(total - 24) > 0.01) {
-    // 浮点数容错
-    callback(new Error(msg))
-  } else {
-    callback()
-  }
-}
-
-const rules = reactive<FormRules>({
-  dailyGasInjection: [{ required: true, message: '请输入当日注气量', trigger: ['blur', 'change'] }],
-  dailyWaterInjection: [
-    { required: true, message: '请输入当日注水量', trigger: ['blur', 'change'] }
-  ],
-  productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['blur', 'change'] }],
-
-  // 时间校验绑定在主要时间字段上
-  dailyInjectGasTime: [{ validator: validateTotalTime, trigger: ['blur', 'change'] }],
-  dailyInjectWaterTime: [{ validator: validateTotalTime, trigger: ['blur', 'change'] }],
-  // 审批意见在审批通过/拒绝时可能需要校验,视业务而定,这里设为非必填或自定义
-  opinion: [{ required: false, message: '请输入审批意见', trigger: 'blur' }]
-})
-
-// 监听所有时间字段变化,触发重新校验
-watch(
-  () => [
-    form.value.dailyInjectGasTime,
-    form.value.dailyInjectWaterTime,
-    ...NON_PROD_FIELDS.map((f) => form.value[f.key as keyof FormOriginal])
-  ],
-  () => {
-    // 只有在非禁用状态下才实时触发校验,避免只读打开时满屏红字
-    if (!isMainFieldDisabled.value) {
-      // 这里的防抖或 nextTick 很重要
-      nextTick(() => {
-        // 触发注气时间的校验即可,因为它关联了整体逻辑
-        formRef.value?.validateField('dailyInjectGasTime')
-      })
-    }
-  },
-  { deep: true }
-)
-
-// --- 提交逻辑 ---
-
-// 1. 编辑/修改 提交
-const submitForm = async () => {
-  if (!formRef.value) return
-  try {
-    await formRef.value.validate()
-    formLoading.value = true
-    const { createTime, ...other } = form.value
-    // 构造提交数据
-    const data = { ...other, fillOrderCreateTime: createTime } as any
-    await IotRhDailyReportApi.createIotRhDailyReport(data)
-
-    message.success(t('common.updateSuccess'))
-    emits('update:visible', false)
-    props.loadList()
-  } catch (error) {
-    console.warn('表单校验未通过或提交出错')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-// 2. 审批 提交 (通过 20 / 拒绝 30)
-const handleAudit = async (auditStatus: 20 | 30) => {
-  if (!formRef.value) return
-
-  // 审批时,可能不需要校验主数据的24小时规则(因为是只读的),只校验意见?
-  // 如果需要校验意见必填,可以在这里手动校验 opinion 字段
-  if (auditStatus === 30 && !form.value.opinion) {
-    message.error('拒绝时请必须填写审批意见')
-    return
-  }
-
-  try {
-    formLoading.value = true
-    const { opinion, id } = form.value
-    const data = { id, auditStatus, opinion }
-
-    await IotRhDailyReportApi.approvalIotRhDailyReport(data)
-    message.success(auditStatus === 20 ? '审批通过成功' : '审批拒绝成功')
-    emits('update:visible', false)
-    props.loadList()
-  } catch (error) {
-    console.warn('审批提交出错', error)
-  } finally {
-    formLoading.value = false
-  }
-}
-</script>
-
-<template>
-  <el-drawer
-    :model-value="visible"
-    @update:model-value="emits('update:visible', $event)"
-    header-class="mb-0!"
-    size="50%"
-  >
-    <template #header>
-      <span class="text-xl font-bold text-[var(--el-text-color-primary)]">
-        {{ type === 'edit' ? '编辑日报' : '审批日报' }}
-      </span>
-    </template>
-
-    <el-form
-      ref="formRef"
-      label-position="top"
-      size="default"
-      :rules="rules"
-      :model="form"
-      v-loading="loading"
-      require-asterisk-position="right"
-      :disabled="formType === 'readonly' && type !== 'approval'"
-    >
-      <!-- 顶部提示区 -->
-      <div class="flex flex-col gap-3 text-sm mb-4">
-        <!-- 运行时效预警 -->
-        <div
-          class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
-        >
-          <div class="flex flex-col gap-2.5">
-            <div class="flex items-center justify-between">
-              <div class="text-gray-600 dark:text-gray-400">
-                <span class="font-bold text-gray-800 dark:text-gray-200">运行时效:</span>
-                当日注气量 / 产能
-              </div>
-              <span
-                class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
-              >
-                >120% 红色预警
-              </span>
-            </div>
-          </div>
-        </div>
-
-        <!-- 历史审批意见:仅在 edit 模式且有意见时显示 -->
-        <div
-          v-if="isEdit && form.opinion"
-          class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
-        >
-          <Icon
-            icon="ep:warning-filled"
-            class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
-          />
-          <div class="flex flex-col">
-            <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500">历史审核意见</h4>
-            <p class="leading-relaxed text-gray-600 dark:text-gray-400">{{ form.opinion }}</p>
-          </div>
-        </div>
-      </div>
-
-      <!-- 表单主体区域 -->
-      <div class="grid grid-cols-2 gap-4">
-        <!-- 基础信息 -->
-        <div class="col-span-2 flex items-center gap-2 mt-2">
-          <div class="bg-[var(--el-color-primary)] w-1 h-5 rounded-full"></div>
-          <div class="text-lg font-medium text-[var(--el-text-color-primary)]">基础信息</div>
-        </div>
-
-        <el-form-item label="施工队伍" prop="deptName">
-          <el-input v-model="form.deptName" disabled />
-        </el-form-item>
-        <el-form-item label="项目" prop="contractName">
-          <el-input v-model="form.contractName" disabled />
-        </el-form-item>
-        <el-form-item label="任务" prop="taskName">
-          <el-input v-model="form.taskName" disabled />
-        </el-form-item>
-        <el-form-item label="搬迁安装天数(D)" prop="relocationDays">
-          <el-input v-model="form.relocationDays" disabled />
-        </el-form-item>
-        <el-form-item label="运行时效" prop="transitTime">
-          <el-input
-            :model-value="transitTime.value"
-            disabled
-            :class="{ 'warning-input': transitTime.original > 1.2 }"
-          />
-        </el-form-item>
-
-        <!-- 核心业务数据:审批模式下禁用 -->
-        <el-form-item label="当日注气量(方)" prop="dailyGasInjection">
-          <el-input-number
-            class="!w-full"
-            :min="0"
-            v-model="form.dailyGasInjection"
-            :controls="false"
-            align="left"
-            :disabled="isMainFieldDisabled"
-          />
-        </el-form-item>
-        <el-form-item label="当日注水量(方)" prop="dailyWaterInjection">
-          <el-input-number
-            class="!w-full"
-            :min="0"
-            v-model="form.dailyWaterInjection"
-            :controls="false"
-            align="left"
-            :disabled="isMainFieldDisabled"
-          />
-        </el-form-item>
-        <el-form-item class="col-span-2" label="生产动态" prop="productionStatus">
-          <el-input
-            v-model="form.productionStatus"
-            type="textarea"
-            autosize
-            maxlength="1000"
-            :disabled="isMainFieldDisabled"
-          />
-        </el-form-item>
-        <el-form-item class="col-span-2" label="备注" prop="remark">
-          <el-input
-            v-model="form.remark"
-            type="textarea"
-            autosize
-            maxlength="1000"
-            :disabled="isMainFieldDisabled"
-          />
-        </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>
-          <div class="text-lg font-medium text-[var(--el-text-color-primary)]"
-            >生产与非生产时间</div
-          >
-        </div>
-
-        <!-- 生产时间 -->
-        <el-form-item label="当日注气时间(H)" prop="dailyInjectGasTime">
-          <el-input-number
-            class="!w-full"
-            :min="0"
-            :max="24"
-            v-model="form.dailyInjectGasTime"
-            :controls="false"
-            align="left"
-            placeholder="注气+非生产=24H"
-            :disabled="isMainFieldDisabled"
-          />
-        </el-form-item>
-        <el-form-item label="当日注水时间(H)" prop="dailyInjectWaterTime">
-          <el-input-number
-            class="!w-full"
-            :min="0"
-            :max="24"
-            v-model="form.dailyInjectWaterTime"
-            :controls="false"
-            align="left"
-            placeholder="注气=0时,注水+非生产=24H"
-            :disabled="isMainFieldDisabled"
-          />
-        </el-form-item>
-
-        <!-- 非生产时间:循环生成 -->
-        <el-form-item
-          v-for="field in NON_PROD_FIELDS"
-          :key="field.key"
-          :label="field.label + '(H)'"
-          :prop="field.key"
-        >
-          <el-input-number
-            class="!w-full"
-            :min="0"
-            :max="24"
-            v-model="form[field.key as keyof FormOriginal] as number"
-            :controls="false"
-            align="left"
-            :disabled="isMainFieldDisabled"
-          />
-        </el-form-item>
-
-        <!-- 其他原因说明 -->
-        <el-form-item class="col-span-2" label="其他非生产原因" prop="otherNptReason">
-          <el-input
-            v-model="form.otherNptReason"
-            placeholder="如果有其他非生产时间,请在此说明"
-            :disabled="isMainFieldDisabled"
-          />
-        </el-form-item>
-
-        <!-- 审批意见输入框:仅在 approval 模式下显示,且只要整体不是 readonly 就可以编辑 -->
-        <div v-if="isApproval" class="col-span-2 mt-4 border-t pt-4">
-          <el-form-item label="审批意见" prop="opinion">
-            <el-input
-              v-model="form.opinion"
-              placeholder="请输入审批意见(拒绝时必填)"
-              :maxlength="1000"
-              type="textarea"
-              :autosize="{ minRows: 3 }"
-              :disabled="formType === 'readonly'"
-            />
-          </el-form-item>
-        </div>
-      </div>
-    </el-form>
-
-    <template #footer>
-      <!-- Edit 模式的按钮 -->
-      <div v-if="type === 'edit'">
-        <el-button
-          type="primary"
-          @click="submitForm"
-          :loading="formLoading"
-          :disabled="formType === 'readonly'"
-        >
-          确 定
-        </el-button>
-        <el-button @click="emits('update:visible', false)">取 消</el-button>
-      </div>
-
-      <!-- Approval 模式的按钮 -->
-      <div v-if="type === 'approval'">
-        <el-button
-          type="primary"
-          @click="handleAudit(20)"
-          :loading="formLoading"
-          :disabled="formType === 'readonly'"
-        >
-          审批通过
-        </el-button>
-        <el-button
-          type="danger"
-          @click="handleAudit(30)"
-          :loading="formLoading"
-          :disabled="formType === 'readonly'"
-        >
-          审批拒绝
-        </el-button>
-        <el-button @click="emits('update:visible', false)">取 消</el-button>
-      </div>
-    </template>
-  </el-drawer>
-</template>
-
-<style scoped>
-.warning-input :deep(.el-input__inner) {
-  font-weight: bold;
-  color: var(--el-color-danger);
-}
-</style>

+ 340 - 164
src/views/pms/iotrhdailyreport/rh-form.vue

@@ -1,6 +1,7 @@
 <script lang="ts" setup generic="T">
 import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
 import { FormInstance, FormRules } from 'element-plus'
+import { computed, reactive, ref, watch, nextTick } from 'vue'
 
 interface Props {
   visible: boolean
@@ -14,8 +15,27 @@ const props = withDefaults(defineProps<Props>(), {
 
 const emits = defineEmits(['update:visible'])
 
+// 1. 定义具体的 11 个非生产时间字段
+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: '其他非生产时间' }
+] as const
+
 interface FormOriginal {
   id: number
+  deptId: number
+  projectId: number
+  taskId: number
   deptName: string
   contractName: string
   taskName: string
@@ -23,8 +43,24 @@ interface FormOriginal {
   dailyWaterInjection: number
   dailyInjectGasTime: number
   dailyInjectWaterTime: number
-  nonProductionTime: number
-  nptReason: string
+
+  // 11个非生产时间字段
+  repairTime: number
+  selfStopTime: number
+  accidentTime: number
+  complexityTime: number
+  rectificationTime: number
+  waitingStopTime: number
+  partyaDesign: number
+  partyaPrepare: number
+  partyaResource: number
+  relocationTime: number
+  winterBreakTime: number
+  otherNptTime: number
+
+  // 其他非生产时间原因(仅作为备注字段存在)
+  otherNptReason: string
+
   productionStatus: string
   remark: string
   relocationDays: number
@@ -33,7 +69,10 @@ interface FormOriginal {
   opinion: string
 }
 
-const FORM_KEYS = [
+type Form = Partial<FormOriginal>
+
+// 字段白名单
+const FORM_KEYS: (keyof FormOriginal)[] = [
   'id',
   'deptId',
   'projectId',
@@ -45,181 +84,249 @@ const FORM_KEYS = [
   'dailyWaterInjection',
   'dailyInjectGasTime',
   'dailyInjectWaterTime',
-  'nonProductionTime',
-  'nptReason',
   'productionStatus',
   'remark',
   'relocationDays',
   'capacity',
   'createTime',
-  'opinion'
+  'opinion',
+  'repairTime',
+  'selfStopTime',
+  'accidentTime',
+  'complexityTime',
+  'rectificationTime',
+  'waitingStopTime',
+  'partyaDesign',
+  'partyaPrepare',
+  'partyaResource',
+  'relocationTime',
+  'winterBreakTime',
+  'otherNptTime',
+  'otherNptReason'
 ]
 
-type Form = Partial<FormOriginal>
-
 const formRef = ref<FormInstance>()
 const loading = ref(false)
 const formLoading = ref(false)
 const formType = ref<'edit' | 'readonly'>('edit')
 const message = useMessage()
+const { t } = useI18n()
 
-const initFormData = (): Form => ({
-  dailyGasInjection: 0,
-  dailyWaterInjection: 0,
-  dailyInjectGasTime: 0,
-  dailyInjectWaterTime: 0,
-  nonProductionTime: 0,
-  relocationDays: 0,
-  capacity: 0
-})
+// 初始化表单
+const initFormData = (): Form => {
+  const base: any = {
+    dailyGasInjection: 0,
+    dailyWaterInjection: 0,
+    dailyInjectGasTime: 0,
+    dailyInjectWaterTime: 0,
+    relocationDays: 0,
+    capacity: 0,
+    otherNptReason: '',
+    opinion: ''
+  }
+  // 初始化所有非生产时间字段为 0
+  NON_PROD_FIELDS.forEach((field) => {
+    base[field.key] = 0
+  })
+  return base as Form
+}
 
 const form = ref<Form>(initFormData())
 
+const isApproval = computed(() => props.type === 'approval')
+const isEdit = computed(() => props.type === 'edit')
+const isMainFieldDisabled = computed(() => formType.value === 'readonly' || isApproval.value)
+
 async function loadDetail(id: number) {
   loading.value = true
   try {
     const res = await IotRhDailyReportApi.getIotRhDailyReport(id)
+    form.value = initFormData()
+    // 按需赋值
     FORM_KEYS.forEach((key) => {
-      form.value[key] = res[key] ?? form.value[key]
+      if (
+        Object.prototype.hasOwnProperty.call(res, key) &&
+        res[key] !== null &&
+        res[key] !== undefined
+      ) {
+        // @ts-ignore
+        form.value[key] = res[key]
+      }
     })
-    form.value.id = id
-
-    if (props.type === 'edit' && res.status !== 0) {
-      formType.value = 'readonly'
-    }
 
-    if (props.type === 'approval' && res.auditStatus !== 10) {
-      formType.value = 'readonly'
-    }
-
-    if (!form.value.capacity) {
-      message.error('请维护增压机产能')
-    }
+    if (props.type === 'edit' && res.status !== 0) formType.value = 'readonly'
+    if (props.type === 'approval' && res.auditStatus !== 10) formType.value = 'readonly'
+    if (!form.value.capacity) message.error('请维护增压机产能')
   } finally {
     loading.value = false
   }
 }
 
 function handleOpenForm(id: number, type: 'edit' | 'readonly') {
-  form.value = initFormData()
-  formRef.value?.resetFields()
-
   formType.value = type
-
   emits('update:visible', true)
   loadDetail(id).then(() => {
-    formRef.value?.validate()
+    nextTick(() => formRef.value?.clearValidate())
   })
 }
 
-defineExpose({
-  handleOpenForm
-})
+defineExpose({ handleOpenForm })
 
+// --- 运行时效 ---
 const transitTime = computed(() => {
   const cap = form.value.capacity
   const gas = form.value.dailyGasInjection ?? 0
-
   if (!cap) return { original: 0, value: '0%' }
-
   const original = gas / cap
   return { original, value: (original * 100).toFixed(2) + '%' }
 })
 
-const sumTimes = () => {
-  const { dailyInjectGasTime = 0, dailyInjectWaterTime = 0, nonProductionTime = 0 } = form.value
-  return parseFloat((dailyInjectGasTime + dailyInjectWaterTime + nonProductionTime).toFixed(2))
+// --- 核心校验逻辑 ---
+
+// 计算所有非生产时间总和
+const sumNonProdTimes = () => {
+  let sum = 0
+  NON_PROD_FIELDS.forEach((field) => {
+    sum += (form.value[field.key as keyof FormOriginal] as number) || 0
+  })
+  return sum
 }
 
-const validateTotalTime = (_rule: any, _value: any, callback: any) => {
-  const total = sumTimes()
-  if (total !== 24) {
-    callback(new Error(`当前合计 ${total} 小时,三项时间之和必须等于 24`))
-  } else {
-    callback()
+// 24小时平衡校验器
+const validateTotalTime =
+  (isNon: boolean = false) =>
+  (_rule: any, _value: any, callback: any) => {
+    const gasTime = form.value.dailyInjectGasTime || 0
+    const waterTime = form.value.dailyInjectWaterTime || 0
+    const nonProdSum = sumNonProdTimes()
+
+    let total = 0
+    let msg = ''
+
+    if (gasTime > 0) {
+      total = parseFloat((gasTime + nonProdSum).toFixed(2))
+      msg = `注气(${gasTime})+非生产(${nonProdSum})=${total}H,必须为24H`
+    } else {
+      total = parseFloat((waterTime + nonProdSum).toFixed(2))
+      msg = `注水(${waterTime})+非生产(${nonProdSum})=${total}H,必须为24H`
+    }
+
+    if (Math.abs(total - 24) > 0.01) {
+      if (!isNon) callback(new Error(msg))
+      else callback(new Error())
+    } else {
+      callback()
+    }
   }
-}
 
-const validateNptReason = (_rule: any, value: any, callback: any) => {
-  if ((form.value.nonProductionTime || 0) > 0 && !value) {
-    callback(new Error('非生产时间大于 0 时,必须选择原因'))
+const validateOtherReason = (_rule: any, value: any, callback: any) => {
+  const time = form.value.otherNptTime || 0
+  if (time > 0 && !value) {
+    callback(new Error('填写了其他时间,必须说明原因'))
   } else {
     callback()
   }
 }
 
-// const timeRuleItem = [
-//   { required: true, message: '请输入时间', trigger: 'blur' },
-//   { validator: validateTotalTime, trigger: 'blur' }
-// ]
-
+// 动态构建校验规则
 const rules = reactive<FormRules>({
-  dailyGasInjection: [{ required: true, message: '请输入当日注气量', trigger: ['change', 'blur'] }],
+  dailyGasInjection: [{ required: true, message: '请输入当日注气量', trigger: ['blur', 'change'] }],
   dailyWaterInjection: [
-    { required: true, message: '请输入当日注水量', trigger: ['change', 'blur'] }
+    { required: true, message: '请输入当日注水量', trigger: ['blur', 'change'] }
   ],
-  productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['change', 'blur'] }],
+  productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['blur', 'change'] }],
 
-  // 复用规则
-  // dailyInjectGasTime: timeRuleItem,
-  // dailyInjectWaterTime: timeRuleItem,
-  // nonProductionTime: timeRuleItem,
+  // 生产时间绑定校验
+  dailyInjectGasTime: [
+    { required: true, message: '请输入当日注气时间', trigger: ['blur', 'change'] },
+    { validator: validateTotalTime(), trigger: ['blur', 'change'] }
+  ],
+  dailyInjectWaterTime: [{ validator: validateTotalTime(), trigger: ['blur', 'change'] }],
+  otherNptReason: [{ validator: validateOtherReason, trigger: ['blur', 'change'] }]
+})
 
-  nptReason: [{ validator: validateNptReason, trigger: ['change', 'blur'] }]
+// 关键步骤:为每一个非生产时间字段都绑定 validateTotalTime 规则
+// 这样当总和不对时,所有时间字段下面都会出现红色错误提示
+NON_PROD_FIELDS.forEach((field) => {
+  rules[field.key] = [{ validator: validateTotalTime(true), trigger: ['blur', 'change'] }]
 })
 
+// 监听所有时间字段
+const allTimeKeys = [
+  'dailyInjectGasTime',
+  'dailyInjectWaterTime',
+  ...NON_PROD_FIELDS.map((f) => f.key)
+]
+
+// 当任一时间变化时,触发所有时间字段的校验更新
 watch(
-  [
-    () => form.value.dailyInjectGasTime,
-    () => form.value.dailyInjectWaterTime,
-    () => form.value.nonProductionTime
-  ],
+  () => allTimeKeys.map((key) => form.value[key as keyof FormOriginal]),
   () => {
-    nextTick(() => {
-      formRef.value?.validateField('nptReason')
-      if (sumTimes() === 24) {
-        formRef.value?.clearValidate([
-          'dailyInjectGasTime',
-          'dailyInjectWaterTime',
-          'nonProductionTime'
-        ])
-      }
-    })
-  }
+    if (!isMainFieldDisabled.value) {
+      nextTick(() => {
+        // 传入数组,同时校验所有字段
+        formRef.value?.validateField(allTimeKeys)
+      })
+    }
+  },
+  { deep: true }
 )
 
-const { t } = useI18n()
-
+// --- 提交 ---
 const submitForm = async () => {
   if (!formRef.value) return
 
   try {
     await formRef.value.validate()
     formLoading.value = true
-    const { createTime, ...other } = form.value
-    const data = { ...other, fillOrderCreateTime: createTime } as any
-    await IotRhDailyReportApi.createIotRhDailyReport(data)
+    const submitData: any = {}
+    FORM_KEYS.forEach((key) => (submitData[key] = form.value[key]))
+    submitData.fillOrderCreateTime = form.value.createTime
+
+    await IotRhDailyReportApi.createIotRhDailyReport(submitData)
     message.success(t('common.updateSuccess'))
     emits('update:visible', false)
     props.loadList()
   } catch (error) {
-    console.warn('表单校验未通过或提交出错')
+    console.warn('校验失败')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+const handleAudit = async (auditStatus: 20 | 30) => {
+  if (!formRef.value) return
+  try {
+    formLoading.value = true
+    await IotRhDailyReportApi.approvalIotRhDailyReport({
+      id: form.value.id!,
+      auditStatus,
+      opinion: form.value.opinion!
+    })
+    message.success(auditStatus === 20 ? '通过成功' : '拒绝成功')
+    emits('update:visible', false)
+    props.loadList()
+  } catch (error) {
+    console.warn(error)
   } finally {
     formLoading.value = false
   }
 }
 </script>
+
 <template>
   <el-drawer
     :model-value="visible"
     @update:model-value="emits('update:visible', $event)"
     header-class="mb-0!"
+    size="50%"
   >
     <template #header>
-      <span class="text-xl font-bold text-[var(--el-text-color-primary)]">{{
-        type === 'edit' ? '编辑' : '审批'
-      }}</span>
+      <span class="text-xl font-bold text-[var(--el-text-color-primary)]">
+        {{ type === 'edit' ? '编辑日报' : '审批日报' }}
+      </span>
     </template>
+
     <el-form
       ref="formRef"
       label-position="top"
@@ -228,9 +335,10 @@ const submitForm = async () => {
       :model="form"
       v-loading="loading"
       require-asterisk-position="right"
-      :disabled="formType === 'readonly'"
+      :disabled="formType === 'readonly' && type !== 'approval'"
     >
-      <div class="flex flex-col gap-3 text-sm">
+      <!-- 顶部提示区 -->
+      <div class="flex flex-col gap-3 text-sm mb-4">
         <div
           class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
         >
@@ -246,21 +354,15 @@ const submitForm = async () => {
                 >120% 红色预警
               </span>
             </div>
-            <!-- <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
-                  注气 + 注水 + 非生产 = 24H
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
-                >
-                  ≠24H 橙色预警
-                </span>
-              </div> -->
+            <!-- 可以显示一个总计提示,辅助用户 -->
+            <!-- <div class="text-gray-600 dark:text-gray-400">
+              <span class="font-bold text-gray-800 dark:text-gray-200">时间校验:</span>
+              (注气 > 0 ? 注气 : 注水) + 所有非生产时间 = 24H
+            </div> -->
           </div>
         </div>
         <div
-          v-if="form.opinion"
+          v-if="isEdit && form.opinion"
           class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
         >
           <Icon
@@ -268,129 +370,203 @@ const submitForm = async () => {
             class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
           />
           <div class="flex flex-col">
-            <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500"> 审核意见 </h4>
-            <p class="leading-relaxed text-gray-600 dark:text-gray-400">
-              {{ form.opinion }}
-            </p>
+            <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500">审核意见</h4>
+            <p class="leading-relaxed text-gray-600 dark:text-gray-400">{{ form.opinion }}</p>
           </div>
         </div>
       </div>
 
-      <div class="grid grid-cols-2 gap-4 mt-4">
-        <div class="flex items-center gap-2 grid-col-span-2">
+      <div class="grid grid-cols-2 gap-4">
+        <!-- 基础信息 -->
+        <div class="col-span-2 flex items-center gap-2 mt-2">
           <div class="bg-[var(--el-color-primary)] w-1 h-5 rounded-full"></div>
-          <div class="text-lg font-medium text-var(--el-text-color-primary)">基础信息</div>
+          <div class="text-lg font-medium text-[var(--el-text-color-primary)]">基础信息</div>
         </div>
-        <el-form-item label="施工队伍" prop="deptName">
-          <el-input v-model="form.deptName" placeholder="" disabled />
-        </el-form-item>
-        <el-form-item label="项目" prop="contractName">
-          <el-input v-model="form.contractName" placeholder="" disabled />
-        </el-form-item>
-        <el-form-item label="任务" prop="taskName">
-          <el-input v-model="form.taskName" placeholder="" disabled />
-        </el-form-item>
-        <el-form-item label="搬迁安装天数(D)" prop="relocationDays">
-          <el-input v-model="form.relocationDays" placeholder="" disabled />
-        </el-form-item>
+        <el-form-item label="施工队伍" prop="deptName"
+          ><el-input v-model="form.deptName" disabled
+        /></el-form-item>
+        <el-form-item label="项目" prop="contractName"
+          ><el-input v-model="form.contractName" disabled
+        /></el-form-item>
+        <el-form-item label="任务" prop="taskName"
+          ><el-input v-model="form.taskName" disabled
+        /></el-form-item>
+        <el-form-item label="搬迁安装天数(D)" prop="relocationDays"
+          ><el-input v-model="form.relocationDays" disabled
+        /></el-form-item>
         <el-form-item label="运行时效" prop="transitTime">
           <el-input
             :model-value="transitTime.value"
-            placeholder="运行时效"
             disabled
             :class="{ 'warning-input': transitTime.original > 1.2 }"
-            id="transitTimeInput"
           />
         </el-form-item>
         <el-form-item label="当日注气量(方)" prop="dailyGasInjection">
           <el-input-number
-            class="w-full!"
+            class="!w-full"
             :min="0"
             v-model="form.dailyGasInjection"
-            placeholder="请输入当日注气量(方)"
-            align="left"
             :controls="false"
+            align="left"
+            :disabled="isMainFieldDisabled"
           />
         </el-form-item>
         <el-form-item label="当日注水量(方)" prop="dailyWaterInjection">
           <el-input-number
-            class="w-full!"
+            class="!w-full"
             :min="0"
             v-model="form.dailyWaterInjection"
-            placeholder="请输入当日注水量(方)"
-            align="left"
             :controls="false"
+            align="left"
+            :disabled="isMainFieldDisabled"
           />
         </el-form-item>
-        <el-form-item class="grid-col-span-2" label="生产动态" prop="productionStatus">
+        <el-form-item class="col-span-2" label="生产动态" prop="productionStatus">
           <el-input
             v-model="form.productionStatus"
-            placeholder="请输入生产动态"
             type="textarea"
             autosize
-            :max-length="1000"
+            maxlength="1000"
+            :disabled="isMainFieldDisabled"
           />
         </el-form-item>
-        <el-form-item class="grid-col-span-2" label="备注" prop="remark">
+        <el-form-item class="col-span-2" label="备注" prop="remark">
           <el-input
             v-model="form.remark"
-            placeholder="请输入备注"
-            :max-length="1000"
             type="textarea"
             autosize
+            maxlength="1000"
+            :disabled="isMainFieldDisabled"
           />
         </el-form-item>
-        <div class="flex items-center gap-2 grid-col-span-2">
+
+        <!-- 时间信息 -->
+        <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>
-          <div class="text-lg font-medium text-var(--el-text-color-primary)">生产时间</div>
+          <div class="text-lg font-medium text-[var(--el-text-color-primary)]"
+            >生产与非生产时间</div
+          >
         </div>
+
         <el-form-item label="当日注气时间(H)" prop="dailyInjectGasTime">
           <el-input-number
-            class="w-full!"
+            class="!w-full"
             :min="0"
+            :max="24"
             v-model="form.dailyInjectGasTime"
-            placeholder="请输入当日注气时间(H)"
-            align="left"
             :controls="false"
+            align="left"
+            placeholder="注气+非生产=24H"
+            :disabled="isMainFieldDisabled"
           />
         </el-form-item>
         <el-form-item label="当日注水时间(H)" prop="dailyInjectWaterTime">
           <el-input-number
-            class="w-full!"
+            class="!w-full"
             :min="0"
+            :max="24"
             v-model="form.dailyInjectWaterTime"
-            placeholder="当日注水时间(H)"
-            align="left"
             :controls="false"
+            align="left"
+            placeholder="注气=0时,注水+非生产=24H"
+            :disabled="isMainFieldDisabled"
           />
         </el-form-item>
-        <!-- <el-form-item label="非生产时间(H)" prop="nonProductionTime">
+
+        <!-- 循环渲染11个具体非生产时间 -->
+        <el-form-item
+          v-for="field in NON_PROD_FIELDS"
+          :key="field.key"
+          :label="field.label + '(H)'"
+          :prop="field.key"
+        >
           <el-input-number
-            class="w-full!"
+            class="!w-full"
             :min="0"
-            v-model="form.nonProductionTime"
-            placeholder="非生产时间(H)"
+            :max="24"
+            v-model="form[field.key as keyof FormOriginal]"
+            :controls="false"
+            align="left"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+
+        <!-- 其他非生产原因 -->
+        <el-form-item class="col-span-2" label="其他非生产原因" prop="otherNptReason">
+          <el-input
+            v-model="form.otherNptReason"
+            placeholder="请输入其他非生产原因"
+            :disabled="isMainFieldDisabled"
           />
         </el-form-item>
-        <el-form-item label="非生产时间原因" prop="nptReason">
-          <el-select v-model="form.nptReason" placeholder="请选择" clearable>
-            <el-option
-              v-for="(dict, index) of getStrDictOptions(DICT_TYPE.PMS_PROJECT_NPT_REASON)"
-              :key="index"
-              :label="dict.label"
-              :value="dict.value"
+
+        <!-- 审批意见 -->
+        <div v-if="isApproval" class="col-span-2 mt-4 border-t pt-4">
+          <el-form-item label="审批意见" prop="opinion">
+            <el-input
+              v-model="form.opinion"
+              placeholder="请输入审批意见"
+              maxlength="1000"
+              type="textarea"
+              :autosize="{ minRows: 3 }"
+              :disabled="formType === 'readonly'"
             />
-          </el-select>
-        </el-form-item> -->
+          </el-form-item>
+        </div>
       </div>
     </el-form>
+
     <template #footer>
-      <el-button size="default" @click="submitForm" type="primary" :disabled="formLoading">
-        确 定
-      </el-button>
-      <el-button size="default" @click="emits('update:visible', false)">取 消</el-button>
+      <div v-if="isEdit">
+        <el-button
+          type="primary"
+          @click="submitForm"
+          :loading="formLoading"
+          :disabled="formType === 'readonly'"
+          >确 定</el-button
+        >
+        <el-button @click="emits('update:visible', false)">取 消</el-button>
+      </div>
+      <div v-if="isApproval">
+        <el-button
+          type="primary"
+          @click="handleAudit(20)"
+          :loading="formLoading"
+          :disabled="formType === 'readonly'"
+          >审批通过</el-button
+        >
+        <el-button
+          type="danger"
+          @click="handleAudit(30)"
+          :loading="formLoading"
+          :disabled="formType === 'readonly'"
+          >审批拒绝</el-button
+        >
+        <el-button @click="emits('update:visible', false)">取 消</el-button>
+      </div>
     </template>
   </el-drawer>
 </template>
 
-<style scoped></style>
+<style scoped>
+: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;
+  }
+}
+</style>

+ 10 - 586
src/views/pms/iotrydailyreport/approval.vue

@@ -2,12 +2,11 @@
 import { rangeShortcuts } from '@/utils/formatTime'
 import { useDebounceFn } from '@vueuse/core'
 import dayjs from 'dayjs'
-import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { DICT_TYPE } from '@/utils/dict'
 import { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults'
-import { FormInstance, FormRules } from 'element-plus'
-import Form from '@/components/Form/src/Form.vue'
 import { useUserStore } from '@/store/modules/user'
 import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import ryForm from './ry-form.vue'
 
 interface List {
   id: number
@@ -516,235 +515,23 @@ watch(
   { immediate: true }
 )
 
-const FORM_KEYS = [
-  'id',
-  'deptName',
-  'contractName',
-  'taskName',
-  'rigStatus',
-  'designWellDepth',
-  'currentDepth',
-  'dailyPowerUsage',
-  'dailyFuel',
-  'mudDensity',
-  'mudViscosity',
-  'lateralLength',
-  'wellInclination',
-  'azimuth',
-  'designWellStruct',
-  'productionStatus',
-  'remark',
-  'createTime',
-  'deptId',
-  'projectId',
-  'taskId',
-  'opinion',
-  'personnel',
-  'accidentTime',
-  'repairTime',
-  'selfStopTime',
-  'complexityTime',
-  'relocationTime',
-  'rectificationTime',
-  'waitingStopTime',
-  'winterBreakTime',
-  'drillingWorkingTime',
-  'otherProductionTime',
-  'lastCurrentDepth'
-] as const
+const visible = ref(false)
 
-type FormKey = (typeof FORM_KEYS)[number]
-type Form = Partial<Pick<List, FormKey>>
+const formRef = ref()
 
-const dialogVisible = ref(false)
-const formRef = ref<FormInstance>()
-const formLoading = ref(false)
-const message = useMessage()
-
-const initFormData = (): Form => ({})
-
-const form = ref<Form>(initFormData())
-
-async function loadDetail(id: number) {
-  try {
-    const res = await IotRyDailyReportApi.getIotRyDailyReport(id)
-    FORM_KEYS.forEach((key) => {
-      form.value[key] = res[key] ?? form.value[key]
-    })
-    form.value.id = id
-
-    if (res.auditStatus !== 10) {
-      formType.value = 'readonly'
-    }
-  } finally {
+function handleOpenForm(id: number, type: 'edit' | 'readonly') {
+  if (formRef.value) {
+    formRef.value.handleOpenForm(id, type)
   }
 }
 
-const formType = ref<'approval' | 'readonly'>('approval')
-
-function handleOpenForm(id: number, type: 'approval' | 'readonly') {
-  form.value = initFormData()
-  formRef.value?.resetFields()
-
-  formType.value = type
-
-  dialogVisible.value = true
-  loadDetail(id).then(() => {
-    formRef.value?.validate()
-  })
-}
-
 const route = useRoute()
 
 onMounted(() => {
   if (Object.keys(route.query).length > 0) {
-    handleOpenForm(Number(route.query.id), 'approval')
-  }
-})
-
-// const transitTime = computed(() => {
-//   const cap = form.value.capacity
-//   const gas = form.value.dailyGasInjection ?? 0
-
-//   if (!cap) return { original: 0, value: '0%' }
-
-//   const original = gas / cap
-//   return { original, value: (original * 100).toFixed(2) + '%' }
-// })
-
-const sumTimes = () => {
-  const {
-    drillingWorkingTime = 0,
-    otherProductionTime = 0,
-    accidentTime = 0,
-    repairTime = 0,
-    selfStopTime = 0,
-    complexityTime = 0,
-    relocationTime = 0,
-    rectificationTime = 0,
-    waitingStopTime = 0,
-    winterBreakTime = 0
-  } = form.value
-  return parseFloat(
-    (
-      drillingWorkingTime +
-      otherProductionTime +
-      accidentTime +
-      repairTime +
-      selfStopTime +
-      complexityTime +
-      relocationTime +
-      rectificationTime +
-      waitingStopTime +
-      winterBreakTime
-    ).toFixed(2)
-  )
-}
-
-const validateTotalTime = (_rule: any, _value: any, callback: any) => {
-  const total = sumTimes()
-  if (total !== 24) {
-    callback(new Error(`当前合计 ${total} 小时,时间之和必须等于 24`))
-  } else {
-    callback()
+    handleOpenForm(Number(route.query.id), 'edit')
   }
-}
-
-// const validateNptReason = (_rule: any, value: any, callback: any) => {
-//   if ((form.value.nonProductionTime || 0) > 0 && !value) {
-//     callback(new Error('非生产时间大于 0 时,必须选择原因'))
-//   } else {
-//     callback()
-//   }
-// }
-
-const validateLastCurrentDepth = (_rule: any, value: any, callback: any) => {
-  if (value && value < (form.value.lastCurrentDepth ?? 0)) {
-    callback(new Error('当前深度需大于等于上一次填报深度'))
-  } else {
-    callback()
-  }
-}
-
-const timeRuleItem = [
-  { required: true, message: '请输入时间', trigger: 'blur' },
-  { validator: validateTotalTime, trigger: 'blur' }
-]
-
-const rules = reactive<FormRules>({
-  currentDepth: [
-    { required: true, message: '请输入当前深度', trigger: ['change', 'blur'] },
-    { validator: validateLastCurrentDepth, trigger: ['change', 'blur'] }
-  ],
-  productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['change', 'blur'] }],
-
-  // 复用规则
-  drillingWorkingTime: timeRuleItem,
-  otherProductionTime: timeRuleItem,
-  accidentTime: timeRuleItem,
-  repairTime: timeRuleItem,
-  selfStopTime: timeRuleItem,
-  complexityTime: timeRuleItem,
-  relocationTime: timeRuleItem,
-  rectificationTime: timeRuleItem,
-  waitingStopTime: timeRuleItem,
-  winterBreakTime: timeRuleItem
 })
-
-watch(
-  [
-    () => form.value.drillingWorkingTime,
-    () => form.value.otherProductionTime,
-    () => form.value.accidentTime,
-    () => form.value.repairTime,
-    () => form.value.selfStopTime,
-    () => form.value.complexityTime,
-    () => form.value.relocationTime,
-    () => form.value.rectificationTime,
-    () => form.value.waitingStopTime,
-    () => form.value.winterBreakTime
-  ],
-  () => {
-    nextTick(() => {
-      if (sumTimes() === 24) {
-        formRef.value?.clearValidate([
-          'drillingWorkingTime',
-          'otherProductionTime',
-          'accidentTime',
-          'repairTime',
-          'selfStopTime',
-          'complexityTime',
-          'relocationTime',
-          'rectificationTime',
-          'waitingStopTime',
-          'winterBreakTime'
-        ])
-      }
-    })
-  }
-)
-
-const submitForm = async (auditStatus: 20 | 30) => {
-  if (!formRef.value) return
-
-  try {
-    // await formRef.value.validate()
-    formLoading.value = true
-    const { opinion, id } = form.value
-
-    const data = { id: id, auditStatus, opinion } as any
-
-    await IotRyDailyReportApi.approvalIotRyDailyReport(data)
-    message.success(auditStatus === 20 ? '通过成功' : '拒绝成功')
-    dialogVisible.value = false
-
-    loadList()
-  } catch (error) {
-    console.warn('表单校验未通过或提交出错')
-  } finally {
-    formLoading.value = false
-  }
-}
 </script>
 
 <template>
@@ -829,7 +616,7 @@ const submitForm = async (auditStatus: 20 | 30) => {
                         v-show="row.auditStatus === 10"
                         link
                         type="primary"
-                        @click="handleOpenForm(row.id, 'approval')"
+                        @click="handleOpenForm(row.id, 'edit')"
                         v-hasPermi="['pms:iot-ry-daily-report:update']"
                       >
                         审批
@@ -857,356 +644,7 @@ const submitForm = async (auditStatus: 20 | 30) => {
         </div>
       </div>
     </div>
-    <Dialog title="编辑" v-model="dialogVisible">
-      <el-form
-        ref="formRef"
-        label-position="top"
-        size="default"
-        :rules="rules"
-        :model="form"
-        v-loading="formLoading"
-        require-asterisk-position="right"
-      >
-        <div class="flex flex-col gap-3 text-sm">
-          <div
-            class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
-          >
-            <div class="flex flex-col gap-2.5">
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200"> 油量消耗:</span>
-                  当日油耗
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
-                >
-                  >15升 红色预警
-                </span>
-              </div>
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
-                  进尺 + 其它生产 + 非生产 = 24H
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
-                >
-                  ≠24H 橙色预警
-                </span>
-              </div>
-            </div>
-          </div>
-          <!-- <div
-          v-if="form.opinion"
-          class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
-        >
-          <Icon
-            icon="ep:warning-filled"
-            class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
-          />
-          <div class="flex flex-col">
-            <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500"> 审核意见 </h4>
-            <p class="leading-relaxed text-gray-600 dark:text-gray-400">
-              {{ form.opinion }}
-            </p>
-          </div>
-        </div> -->
-        </div>
-        <div class="grid grid-cols-2 gap-4 mt-5">
-          <el-form-item label="施工队伍" prop="deptName">
-            <el-input v-model="form.deptName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="项目" prop="contractName">
-            <el-input v-model="form.contractName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="任务" prop="taskName">
-            <el-input v-model="form.taskName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="施工状态" prop="rigStatus">
-            <el-select v-model="form.rigStatus" placeholder="请选择施工状态" disabled>
-              <el-option
-                v-for="(dict, index) in getStrDictOptions(DICT_TYPE.PMS_PROJECT_TASK_RY_SCHEDULE)"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="设计井深(m)" prop="designWellDepth">
-            <el-input v-model="form.designWellDepth" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="当前井深(m)" prop="currentDepth">
-            <el-input-number
-              class="placeholder-"
-              :min="0"
-              v-model="form.currentDepth"
-              placeholder="请输入当前井深(m)"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="当日用电量(kWh)" prop="dailyPowerUsage">
-            <el-input-number
-              :min="0"
-              v-model="form.dailyPowerUsage"
-              placeholder="请输入当日用电量(kWh)"
-              clearable
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="当日油耗(升)" prop="dailyFuel">
-            <el-input-number
-              :min="0"
-              v-model="form.dailyFuel"
-              placeholder="请输入当日油耗(升)"
-              clearable
-              :class="{ 'warning-input': (form.dailyFuel ?? 0) > 15 }"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="泥浆密度(g/cm³)" prop="mudDensity">
-            <el-input-number
-              :min="0"
-              v-model="form.mudDensity"
-              placeholder="请输入泥浆性能-密度(g/cm³)"
-              clearable
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="泥浆粘度(S)" prop="mudViscosity">
-            <el-input-number
-              :min="0"
-              v-model="form.mudViscosity"
-              placeholder="请输入泥浆性能-粘度(S)"
-              clearable
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="水平段长度(m)" prop="lateralLength">
-            <el-input-number
-              :min="0"
-              v-model="form.lateralLength"
-              placeholder="请输入水平段长度(m)"
-              clearable
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="井斜(°)" prop="wellInclination">
-            <el-input-number
-              :min="0"
-              v-model="form.wellInclination"
-              placeholder="请输入井斜(°)"
-              clearable
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="方位(°)" prop="azimuth">
-            <el-input-number
-              :min="0"
-              v-model="form.azimuth"
-              placeholder="请输入方位(°)"
-              clearable
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="设计井身结构" prop="designWellStruct">
-            <el-input
-              v-model="form.designWellStruct"
-              placeholder=""
-              type="textarea"
-              disabled
-              autosize
-            />
-          </el-form-item>
-          <!-- <el-form-item label="生产动态" prop="productionStatus">
-          <el-input
-            v-model="form.productionStatus"
-            placeholder="请输入生产动态"
-            type="textarea"
-            autosize
-            :max-length="1000"
-            disabled
-          />
-        </el-form-item> -->
-          <el-form-item label="人员情况" prop="personnel">
-            <el-input
-              v-model="form.personnel"
-              placeholder="请输入人员情况"
-              type="textarea"
-              :max-length="1000"
-              autosize
-              disabled
-            />
-          </el-form-item>
-          <!-- <el-form-item label="备注" prop="remark">
-          <el-input
-            v-model="form.remark"
-            placeholder="请输入备注"
-            :max-length="1000"
-            type="textarea"
-            autosize
-            disabled
-          />
-        </el-form-item> -->
-        </div>
-        <el-divider content-position="left">生产时间</el-divider>
-        <div class="grid grid-cols-2 gap-4 mt-5">
-          <el-form-item label="进尺工作时间(H)" prop="drillingWorkingTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.drillingWorkingTime"
-              placeholder="进尺工作时间(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="其它生产时间(H)" prop="otherProductionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.otherProductionTime"
-              placeholder="其它生产时间(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-        </div>
-        <el-divider content-position="left">非生产时间</el-divider>
-        <div class="grid grid-cols-4 gap-4 mt-5">
-          <el-form-item label="事故(H)" prop="accidentTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.accidentTime"
-              placeholder="请输入事故(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="修理(H)" prop="repairTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.repairTime"
-              placeholder="请输入修理(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="自停(H)" prop="selfStopTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.selfStopTime"
-              placeholder="请输入自停(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="复杂(H)" prop="complexityTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.complexityTime"
-              placeholder="请输入复杂(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="搬迁(H)" prop="relocationTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.relocationTime"
-              placeholder="请输入搬迁(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="整改(H)" prop="rectificationTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.rectificationTime"
-              placeholder="请输入整改(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="等停(H)" prop="waitingStopTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.waitingStopTime"
-              placeholder="请输入等停(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="冬休(H)" prop="winterBreakTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.winterBreakTime"
-              placeholder="请输入冬休(H)"
-              disabled
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-        </div>
-        <div class="grid grid-cols-1 gap-4 mt-5">
-          <el-form-item label="生产动态" prop="productionStatus">
-            <el-input
-              v-model="form.productionStatus"
-              placeholder="请输入生产动态"
-              type="textarea"
-              autosize
-              :max-length="1000"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="备注" prop="remark">
-            <el-input
-              v-model="form.remark"
-              placeholder="请输入备注"
-              :max-length="1000"
-              type="textarea"
-              autosize
-              disabled
-            />
-          </el-form-item>
-        </div>
-        <el-form-item class="mt-4" label="审批意见" prop="opinion">
-          <el-input
-            v-model="form.opinion"
-            placeholder="请输入审批意见"
-            :max-length="1000"
-            type="textarea"
-            autosize
-            :disabled="formType === 'readonly'"
-          />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button
-          size="default"
-          @click="submitForm(20)"
-          type="primary"
-          :disabled="formLoading || formType === 'readonly'"
-        >
-          审批通过
-        </el-button>
-        <el-button
-          size="default"
-          @click="submitForm(30)"
-          type="danger"
-          :disabled="formLoading || formType === 'readonly'"
-        >
-          审批拒绝
-        </el-button>
-        <el-button size="default" @click="dialogVisible = false">取 消</el-button>
-      </template>
-    </Dialog>
+    <ry-form v-model:visible="visible" type="approval" ref="formRef" :load-list="loadList" />
   </div>
 </template>
 
@@ -1230,20 +668,6 @@ const submitForm = async (auditStatus: 20 | 30) => {
   }
 }
 
-:deep(.warning-input) {
-  .el-input__inner {
-    color: red !important;
-    -webkit-text-fill-color: red !important;
-  }
-}
-
-:deep(.orange-input) {
-  .el-input__inner {
-    color: orange !important;
-    -webkit-text-fill-color: orange !important;
-  }
-}
-
 :deep(.el-input-number) {
   width: 100%;
 }

+ 8 - 520
src/views/pms/iotrydailyreport/fill.vue

@@ -2,12 +2,11 @@
 import { rangeShortcuts } from '@/utils/formatTime'
 import { useDebounceFn } from '@vueuse/core'
 import dayjs from 'dayjs'
-import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { DICT_TYPE } from '@/utils/dict'
 import { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults'
-import { FormInstance, FormRules } from 'element-plus'
-import Form from '@/components/Form/src/Form.vue'
 import { useUserStore } from '@/store/modules/user'
 import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import ryForm from './ry-form.vue'
 
 interface List {
   id: number
@@ -516,82 +515,14 @@ watch(
   { immediate: true }
 )
 
-const FORM_KEYS = [
-  'id',
-  'deptName',
-  'contractName',
-  'taskName',
-  'rigStatus',
-  'designWellDepth',
-  'currentDepth',
-  'dailyPowerUsage',
-  'dailyFuel',
-  'mudDensity',
-  'mudViscosity',
-  'lateralLength',
-  'wellInclination',
-  'azimuth',
-  'designWellStruct',
-  'productionStatus',
-  'remark',
-  'createTime',
-  'deptId',
-  'projectId',
-  'taskId',
-  'opinion',
-  'personnel',
-  'accidentTime',
-  'repairTime',
-  'selfStopTime',
-  'complexityTime',
-  'relocationTime',
-  'rectificationTime',
-  'waitingStopTime',
-  'winterBreakTime',
-  'drillingWorkingTime',
-  'otherProductionTime',
-  'lastCurrentDepth'
-] as const
+const visible = ref(false)
 
-type FormKey = (typeof FORM_KEYS)[number]
-type Form = Partial<Pick<List, FormKey>>
-
-const dialogVisible = ref(false)
-const formRef = ref<FormInstance>()
-const formLoading = ref(false)
-const message = useMessage()
-
-const initFormData = (): Form => ({})
-
-const form = ref<Form>(initFormData())
-
-async function loadDetail(id: number) {
-  try {
-    const res = await IotRyDailyReportApi.getIotRyDailyReport(id)
-    FORM_KEYS.forEach((key) => {
-      form.value[key] = res[key] ?? form.value[key]
-    })
-    form.value.id = id
-
-    if (res.status !== 0) {
-      formType.value = 'readonly'
-    }
-  } finally {
-  }
-}
-
-const formType = ref<'edit' | 'readonly'>('edit')
+const formRef = ref()
 
 function handleOpenForm(id: number, type: 'edit' | 'readonly') {
-  form.value = initFormData()
-  formRef.value?.resetFields()
-
-  formType.value = type
-
-  dialogVisible.value = true
-  loadDetail(id).then(() => {
-    formRef.value?.validate()
-  })
+  if (formRef.value) {
+    formRef.value.handleOpenForm(id, type)
+  }
 }
 
 const route = useRoute()
@@ -601,149 +532,6 @@ onMounted(() => {
     handleOpenForm(Number(route.query.id), 'edit')
   }
 })
-
-// const transitTime = computed(() => {
-//   const cap = form.value.capacity
-//   const gas = form.value.dailyGasInjection ?? 0
-
-//   if (!cap) return { original: 0, value: '0%' }
-
-//   const original = gas / cap
-//   return { original, value: (original * 100).toFixed(2) + '%' }
-// })
-
-const sumTimes = () => {
-  const {
-    drillingWorkingTime = 0,
-    otherProductionTime = 0,
-    accidentTime = 0,
-    repairTime = 0,
-    selfStopTime = 0,
-    complexityTime = 0,
-    relocationTime = 0,
-    rectificationTime = 0,
-    waitingStopTime = 0,
-    winterBreakTime = 0
-  } = form.value
-  return parseFloat(
-    (
-      drillingWorkingTime +
-      otherProductionTime +
-      accidentTime +
-      repairTime +
-      selfStopTime +
-      complexityTime +
-      relocationTime +
-      rectificationTime +
-      waitingStopTime +
-      winterBreakTime
-    ).toFixed(2)
-  )
-}
-
-const validateTotalTime = (_rule: any, _value: any, callback: any) => {
-  const total = sumTimes()
-  if (total !== 24) {
-    callback(new Error(`当前合计 ${total} 小时,时间之和必须等于 24`))
-  } else {
-    callback()
-  }
-}
-
-// const validateNptReason = (_rule: any, value: any, callback: any) => {
-//   if ((form.value.nonProductionTime || 0) > 0 && !value) {
-//     callback(new Error('非生产时间大于 0 时,必须选择原因'))
-//   } else {
-//     callback()
-//   }
-// }
-
-const validateLastCurrentDepth = (_rule: any, value: any, callback: any) => {
-  if (value && value < (form.value.lastCurrentDepth ?? 0)) {
-    callback(new Error(`当前深度需大于等于上一次填报深度 ${form.value.lastCurrentDepth}`))
-  } else {
-    callback()
-  }
-}
-
-const timeRuleItem = [
-  { required: true, message: '请输入时间', trigger: 'blur' },
-  { validator: validateTotalTime, trigger: 'blur' }
-]
-
-const rules = reactive<FormRules>({
-  currentDepth: [
-    { required: true, message: '请输入当前深度', trigger: ['change', 'blur'] },
-    { validator: validateLastCurrentDepth, trigger: ['change', 'blur'] }
-  ],
-  productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['change', 'blur'] }],
-
-  // 复用规则
-  drillingWorkingTime: timeRuleItem,
-  otherProductionTime: timeRuleItem,
-  accidentTime: timeRuleItem,
-  repairTime: timeRuleItem,
-  selfStopTime: timeRuleItem,
-  complexityTime: timeRuleItem,
-  relocationTime: timeRuleItem,
-  rectificationTime: timeRuleItem,
-  waitingStopTime: timeRuleItem,
-  winterBreakTime: timeRuleItem
-})
-
-watch(
-  [
-    () => form.value.drillingWorkingTime,
-    () => form.value.otherProductionTime,
-    () => form.value.accidentTime,
-    () => form.value.repairTime,
-    () => form.value.selfStopTime,
-    () => form.value.complexityTime,
-    () => form.value.relocationTime,
-    () => form.value.rectificationTime,
-    () => form.value.waitingStopTime,
-    () => form.value.winterBreakTime
-  ],
-  () => {
-    nextTick(() => {
-      if (sumTimes() === 24) {
-        formRef.value?.clearValidate([
-          'drillingWorkingTime',
-          'otherProductionTime',
-          'accidentTime',
-          'repairTime',
-          'selfStopTime',
-          'complexityTime',
-          'relocationTime',
-          'rectificationTime',
-          'waitingStopTime',
-          'winterBreakTime'
-        ])
-      }
-    })
-  }
-)
-
-const { t } = useI18n()
-
-const submitForm = async () => {
-  if (!formRef.value) return
-
-  try {
-    await formRef.value.validate()
-    formLoading.value = true
-    const { createTime, ...other } = form.value
-    const data = { ...other, fillOrderCreateTime: createTime, projectClassification: '1' } as any
-    await IotRyDailyReportApi.createIotRyDailyReport(data)
-    message.success(t('common.updateSuccess'))
-    dialogVisible.value = false
-    loadList()
-  } catch (error) {
-    console.warn('表单校验未通过或提交出错')
-  } finally {
-    formLoading.value = false
-  }
-}
 </script>
 
 <template>
@@ -856,293 +644,7 @@ const submitForm = async () => {
         </div>
       </div>
     </div>
-    <Dialog title="编辑" v-model="dialogVisible">
-      <el-form
-        ref="formRef"
-        label-position="top"
-        size="default"
-        :rules="rules"
-        :model="form"
-        v-loading="formLoading"
-        require-asterisk-position="right"
-        :disabled="formType === 'readonly'"
-      >
-        <div class="flex flex-col gap-3 text-sm">
-          <div
-            class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
-          >
-            <div class="flex flex-col gap-2.5">
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200"> 油量消耗:</span>
-                  当日油耗
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
-                >
-                  >15升 红色预警
-                </span>
-              </div>
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
-                  进尺 + 其它生产 + 非生产 = 24H
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
-                >
-                  ≠24H 橙色预警
-                </span>
-              </div>
-            </div>
-          </div>
-          <div
-            v-if="form.opinion"
-            class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
-          >
-            <Icon
-              icon="ep:warning-filled"
-              class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
-            />
-            <div class="flex flex-col">
-              <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500"> 审核意见 </h4>
-              <p class="leading-relaxed text-gray-600 dark:text-gray-400">
-                {{ form.opinion }}
-              </p>
-            </div>
-          </div>
-        </div>
-        <div class="grid grid-cols-2 gap-4 mt-5">
-          <el-form-item label="施工队伍" prop="deptName">
-            <el-input v-model="form.deptName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="项目" prop="contractName">
-            <el-input v-model="form.contractName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="任务" prop="taskName">
-            <el-input v-model="form.taskName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="施工状态" prop="rigStatus">
-            <el-select v-model="form.rigStatus" placeholder="请选择施工状态">
-              <el-option
-                v-for="(dict, index) in getStrDictOptions(DICT_TYPE.PMS_PROJECT_TASK_RY_SCHEDULE)"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="设计井深(m)" prop="designWellDepth">
-            <el-input v-model="form.designWellDepth" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="当前井深(m)" prop="currentDepth">
-            <el-input-number
-              class="placeholder-"
-              :min="0"
-              v-model="form.currentDepth"
-              placeholder="请输入当前井深(m)"
-            />
-          </el-form-item>
-          <el-form-item label="当日用电量(kWh)" prop="dailyPowerUsage">
-            <el-input-number
-              :min="0"
-              v-model="form.dailyPowerUsage"
-              placeholder="请输入当日用电量(kWh)"
-              clearable
-            />
-          </el-form-item>
-          <el-form-item label="当日油耗(升)" prop="dailyFuel">
-            <el-input-number
-              :min="0"
-              v-model="form.dailyFuel"
-              placeholder="请输入当日油耗(升)"
-              clearable
-              :class="{ 'warning-input': (form.dailyFuel ?? 0) > 15 }"
-            />
-          </el-form-item>
-          <el-form-item label="泥浆密度(g/cm³)" prop="mudDensity">
-            <el-input-number
-              :min="0"
-              v-model="form.mudDensity"
-              placeholder="请输入泥浆性能-密度(g/cm³)"
-              clearable
-            />
-          </el-form-item>
-          <el-form-item label="泥浆粘度(S)" prop="mudViscosity">
-            <el-input-number
-              :min="0"
-              v-model="form.mudViscosity"
-              placeholder="请输入泥浆性能-粘度(S)"
-              clearable
-            />
-          </el-form-item>
-          <el-form-item label="水平段长度(m)" prop="lateralLength">
-            <el-input-number
-              :min="0"
-              v-model="form.lateralLength"
-              placeholder="请输入水平段长度(m)"
-              clearable
-            />
-          </el-form-item>
-          <el-form-item label="井斜(°)" prop="wellInclination">
-            <el-input-number
-              :min="0"
-              v-model="form.wellInclination"
-              placeholder="请输入井斜(°)"
-              clearable
-            />
-          </el-form-item>
-          <el-form-item label="方位(°)" prop="azimuth">
-            <el-input-number
-              :min="0"
-              v-model="form.azimuth"
-              placeholder="请输入方位(°)"
-              clearable
-            />
-          </el-form-item>
-          <el-form-item label="设计井身结构" prop="designWellStruct">
-            <el-input
-              v-model="form.designWellStruct"
-              placeholder=""
-              type="textarea"
-              disabled
-              autosize
-            />
-          </el-form-item>
-          <el-form-item label="人员情况" prop="personnel">
-            <el-input
-              v-model="form.personnel"
-              placeholder="请输入人员情况"
-              type="textarea"
-              :max-length="1000"
-              autosize
-            />
-          </el-form-item>
-        </div>
-        <el-divider content-position="left">生产时间</el-divider>
-        <div class="grid grid-cols-2 gap-4 mt-5">
-          <el-form-item label="进尺工作时间(H)" prop="drillingWorkingTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.drillingWorkingTime"
-              placeholder="进尺工作时间(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="其它生产时间(H)" prop="otherProductionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.otherProductionTime"
-              placeholder="其它生产时间(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-        </div>
-        <el-divider content-position="left">非生产时间</el-divider>
-        <div class="grid grid-cols-4 gap-4 mt-5">
-          <el-form-item label="事故(H)" prop="accidentTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.accidentTime"
-              placeholder="请输入事故(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="修理(H)" prop="repairTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.repairTime"
-              placeholder="请输入修理(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="自停(H)" prop="selfStopTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.selfStopTime"
-              placeholder="请输入自停(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="复杂(H)" prop="complexityTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.complexityTime"
-              placeholder="请输入复杂(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="搬迁(H)" prop="relocationTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.relocationTime"
-              placeholder="请输入搬迁(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="整改(H)" prop="rectificationTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.rectificationTime"
-              placeholder="请输入整改(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="等停(H)" prop="waitingStopTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.waitingStopTime"
-              placeholder="请输入等停(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-          <el-form-item label="冬休(H)" prop="winterBreakTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.winterBreakTime"
-              placeholder="请输入冬休(H)"
-              :class="{ 'orange-input': sumTimes() !== 24 }"
-            />
-          </el-form-item>
-        </div>
-        <div class="grid grid-cols-1 gap-4 mt-5">
-          <el-form-item label="生产动态" prop="productionStatus">
-            <el-input
-              v-model="form.productionStatus"
-              placeholder="请输入生产动态"
-              type="textarea"
-              autosize
-              :max-length="1000"
-            />
-          </el-form-item>
-          <el-form-item label="备注" prop="remark">
-            <el-input
-              v-model="form.remark"
-              placeholder="请输入备注"
-              :max-length="1000"
-              type="textarea"
-              autosize
-            />
-          </el-form-item>
-        </div>
-      </el-form>
-      <template #footer>
-        <el-button size="default" @click="submitForm" type="primary" :disabled="formLoading">
-          确 定
-        </el-button>
-        <el-button size="default" @click="dialogVisible = false">取 消</el-button>
-      </template>
-    </Dialog>
+    <ry-form v-model:visible="visible" type="edit" ref="formRef" :load-list="loadList" />
   </div>
 </template>
 
@@ -1166,20 +668,6 @@ const submitForm = async () => {
   }
 }
 
-:deep(.warning-input) {
-  .el-input__inner {
-    color: red !important;
-    -webkit-text-fill-color: red !important;
-  }
-}
-
-:deep(.orange-input) {
-  .el-input__inner {
-    color: orange !important;
-    -webkit-text-fill-color: orange !important;
-  }
-}
-
 :deep(.el-input-number) {
   width: 100%;
 }

+ 694 - 0
src/views/pms/iotrydailyreport/ry-form.vue

@@ -0,0 +1,694 @@
+<script lang="ts" setup generic="T">
+import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { FormInstance, FormRules } from 'element-plus'
+import { computed, reactive, ref, watch, nextTick } from 'vue'
+
+interface Props {
+  visible: boolean
+  type?: 'edit' | 'approval' | 'readonly'
+  loadList: () => void
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  type: 'edit'
+})
+
+const emits = defineEmits(['update:visible'])
+
+// 1. 定义具体的 11 个非生产时间字段
+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: '其他非生产时间' }
+] as const
+
+interface FormOriginal {
+  id: number
+  deptId: number
+  projectId: number
+  taskId: number
+  deptName: string
+  contractName: string
+  taskName: string
+  rigStatus: string
+  designWellDepth: string
+  currentDepth: number
+  dailyPowerUsage: number
+  dailyFuel: number
+  mudDensity: number
+  mudViscosity: number
+  lateralLength: number
+  wellInclination: number
+  azimuth: number
+  designWellStruct: string
+  personnel: string
+  drillingWorkingTime: number
+  otherProductionTime: number
+  lastCurrentDepth: number
+
+  // 11个非生产时间字段
+  repairTime: number
+  selfStopTime: number
+  accidentTime: number
+  complexityTime: number
+  rectificationTime: number
+  waitingStopTime: number
+  partyaDesign: number
+  partyaPrepare: number
+  partyaResource: number
+  relocationTime: number
+  winterBreakTime: number
+  otherNptTime: number
+
+  // 其他非生产时间原因(仅作为备注字段存在)
+  otherNptReason: string
+
+  productionStatus: string
+  remark: string
+  createTime: string
+  opinion: string
+}
+
+type Form = Partial<FormOriginal>
+
+// 字段白名单
+const FORM_KEYS: (keyof FormOriginal)[] = [
+  'id',
+  'deptId',
+  'projectId',
+  'taskId',
+  'deptName',
+  'contractName',
+  'taskName',
+  'rigStatus',
+  'designWellDepth',
+  'currentDepth',
+  'dailyPowerUsage',
+  'dailyFuel',
+  'mudDensity',
+  'mudViscosity',
+  'lateralLength',
+  'wellInclination',
+  'azimuth',
+  'designWellStruct',
+  'personnel',
+  'drillingWorkingTime',
+  'otherProductionTime',
+  'lastCurrentDepth',
+
+  'productionStatus',
+  'remark',
+  'createTime',
+
+  'opinion',
+  'repairTime',
+  'selfStopTime',
+  'accidentTime',
+  'complexityTime',
+  'rectificationTime',
+  'waitingStopTime',
+  'partyaDesign',
+  'partyaPrepare',
+  'partyaResource',
+  'relocationTime',
+  'winterBreakTime',
+  'otherNptTime',
+  'otherNptReason'
+]
+
+const formRef = ref<FormInstance>()
+const loading = ref(false)
+const formLoading = ref(false)
+const formType = ref<'edit' | 'readonly'>('edit')
+const message = useMessage()
+const { t } = useI18n()
+
+// 初始化表单
+const initFormData = (): Form => {
+  const base: any = {
+    drillingWorkingTime: 0,
+    otherProductionTime: 0,
+    otherNptReason: '',
+    opinion: ''
+  }
+  // 初始化所有非生产时间字段为 0
+  NON_PROD_FIELDS.forEach((field) => {
+    base[field.key] = 0
+  })
+  return base as Form
+}
+
+const form = ref<Form>(initFormData())
+
+const isApproval = computed(() => props.type === 'approval')
+const isEdit = computed(() => props.type === 'edit')
+const isMainFieldDisabled = computed(() => formType.value === 'readonly' || isApproval.value)
+
+async function loadDetail(id: number) {
+  loading.value = true
+  try {
+    const res = await IotRyDailyReportApi.getIotRyDailyReport(id)
+    form.value = initFormData()
+    // 按需赋值
+    FORM_KEYS.forEach((key) => {
+      if (
+        Object.prototype.hasOwnProperty.call(res, key) &&
+        res[key] !== null &&
+        res[key] !== undefined
+      ) {
+        // @ts-ignore
+        form.value[key] = res[key]
+      }
+    })
+
+    if (props.type === 'edit' && res.status !== 0) formType.value = 'readonly'
+    if (props.type === 'approval' && res.auditStatus !== 10) formType.value = 'readonly'
+  } finally {
+    loading.value = false
+  }
+}
+
+function handleOpenForm(id: number, type: 'edit' | 'readonly') {
+  formType.value = type
+  emits('update:visible', true)
+  loadDetail(id).then(() => {
+    nextTick(() => formRef.value?.clearValidate())
+  })
+}
+
+defineExpose({ handleOpenForm })
+
+// --- 运行时效 ---
+// const transitTime = computed(() => {
+//   const cap = form.value.capacity
+//   const gas = form.value.dailyGasInjection ?? 0
+//   if (!cap) return { original: 0, value: '0%' }
+//   const original = gas / cap
+//   return { original, value: (original * 100).toFixed(2) + '%' }
+// })
+
+// --- 核心校验逻辑 ---
+
+// 计算所有非生产时间总和
+const sumNonProdTimes = () => {
+  let sum = 0
+  NON_PROD_FIELDS.forEach((field) => {
+    sum += (form.value[field.key as keyof FormOriginal] as number) || 0
+  })
+
+  return sum
+}
+
+// 24小时平衡校验器
+const validateTotalTime =
+  (isNon: boolean = false) =>
+  (_rule: any, _value: any, callback: any) => {
+    const drillingTime = form.value.drillingWorkingTime || 0
+    const otherTime = form.value.otherProductionTime || 0
+
+    const nonProdSum = sumNonProdTimes()
+
+    let total = 0
+    let msg = ''
+
+    total = parseFloat((drillingTime + nonProdSum).toFixed(2))
+    msg = `进尺(${drillingTime})+其他(${otherTime})+非生产(${nonProdSum})=${total}H,必须为24H`
+
+    if (Math.abs(total - 24) > 0.01) {
+      if (!isNon) callback(new Error(msg))
+      else callback(new Error())
+    } else {
+      callback()
+    }
+  }
+
+const validateOtherReason = (_rule: any, value: any, callback: any) => {
+  const time = form.value.otherNptTime || 0
+  if (time > 0 && !value) {
+    callback(new Error('填写了其他时间,必须说明原因'))
+  } else {
+    callback()
+  }
+}
+
+const validateLastCurrentDepth = (_rule: any, value: any, callback: any) => {
+  if (value && value < (form.value.lastCurrentDepth ?? 0)) {
+    callback(new Error(`当前深度需大于等于上一次填报深度 ${form.value.lastCurrentDepth}`))
+  } else {
+    callback()
+  }
+}
+
+// 动态构建校验规则
+const rules = reactive<FormRules>({
+  currentDepth: [
+    { required: true, message: '请输入当前深度', trigger: ['change', 'blur'] },
+    { validator: validateLastCurrentDepth, trigger: ['change', 'blur'] }
+  ],
+  productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['blur', 'change'] }],
+
+  // 生产时间绑定校验
+  drillingWorkingTime: [
+    { required: true, message: '请输入进尺工作时间', trigger: ['blur', 'change'] },
+    { validator: validateTotalTime(), trigger: ['blur', 'change'] }
+  ],
+  otherProductionTime: [{ validator: validateTotalTime(), trigger: ['blur', 'change'] }],
+  otherNptReason: [{ validator: validateOtherReason, trigger: ['blur', 'change'] }]
+})
+
+// 关键步骤:为每一个非生产时间字段都绑定 validateTotalTime 规则
+// 这样当总和不对时,所有时间字段下面都会出现红色错误提示
+NON_PROD_FIELDS.forEach((field) => {
+  rules[field.key] = [{ validator: validateTotalTime(true), trigger: ['blur', 'change'] }]
+})
+
+// 监听所有时间字段
+const allTimeKeys = [
+  'drillingWorkingTime',
+  'otherProductionTime',
+  ...NON_PROD_FIELDS.map((f) => f.key)
+]
+
+// 当任一时间变化时,触发所有时间字段的校验更新
+watch(
+  () => allTimeKeys.map((key) => form.value[key as keyof FormOriginal]),
+  () => {
+    if (!isMainFieldDisabled.value) {
+      nextTick(() => {
+        // 传入数组,同时校验所有字段
+        formRef.value?.validateField(allTimeKeys)
+      })
+    }
+  },
+  { deep: true }
+)
+
+// --- 提交 ---
+const submitForm = async () => {
+  if (!formRef.value) return
+
+  try {
+    await formRef.value.validate()
+    formLoading.value = true
+    const submitData: any = {}
+    FORM_KEYS.forEach((key) => (submitData[key] = form.value[key]))
+    submitData.fillOrderCreateTime = form.value.createTime
+    submitData.projectClassification = '1'
+
+    await IotRyDailyReportApi.createIotRyDailyReport(submitData)
+    message.success(t('common.updateSuccess'))
+    emits('update:visible', false)
+    props.loadList()
+  } catch (error) {
+    console.warn('校验失败')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+const handleAudit = async (auditStatus: 20 | 30) => {
+  if (!formRef.value) return
+  try {
+    formLoading.value = true
+    await IotRyDailyReportApi.approvalIotRyDailyReport({
+      id: form.value.id!,
+      auditStatus,
+      opinion: form.value.opinion!
+    })
+    message.success(auditStatus === 20 ? '通过成功' : '拒绝成功')
+    emits('update:visible', false)
+    props.loadList()
+  } catch (error) {
+    console.warn(error)
+  } finally {
+    formLoading.value = false
+  }
+}
+</script>
+
+<template>
+  <el-drawer
+    :model-value="visible"
+    @update:model-value="emits('update:visible', $event)"
+    header-class="mb-0!"
+    size="50%"
+  >
+    <template #header>
+      <span class="text-xl font-bold text-[var(--el-text-color-primary)]">
+        {{ type === 'edit' ? '编辑日报' : '审批日报' }}
+      </span>
+    </template>
+
+    <el-form
+      ref="formRef"
+      label-position="top"
+      size="default"
+      :rules="rules"
+      :model="form"
+      v-loading="loading"
+      require-asterisk-position="right"
+      :disabled="formType === 'readonly' && type !== 'approval'"
+    >
+      <!-- 顶部提示区 -->
+      <div class="flex flex-col gap-3 text-sm mb-4">
+        <div
+          class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
+        >
+          <div class="flex flex-col gap-2.5">
+            <div class="flex items-center justify-between">
+              <div class="text-gray-600 dark:text-gray-400">
+                <span class="font-bold text-gray-800 dark:text-gray-200"> 油量消耗:</span>
+                当日油耗
+              </div>
+              <span
+                class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
+              >
+                >9000升 红色预警
+              </span>
+            </div>
+            <div class="flex items-center justify-between">
+              <div class="text-gray-600 dark:text-gray-400">
+                <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
+                进尺 + 其它生产 + 非生产 = 24H
+              </div>
+              <span
+                class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
+              >
+                ≠24H 橙色预警
+              </span>
+            </div>
+          </div>
+        </div>
+        <div
+          v-if="isEdit && form.opinion"
+          class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
+        >
+          <Icon
+            icon="ep:warning-filled"
+            class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
+          />
+          <div class="flex flex-col">
+            <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500">审核意见</h4>
+            <p class="leading-relaxed text-gray-600 dark:text-gray-400">{{ form.opinion }}</p>
+          </div>
+        </div>
+      </div>
+
+      <div class="grid grid-cols-2 gap-4">
+        <!-- 基础信息 -->
+        <div class="col-span-2 flex items-center gap-2 mt-2">
+          <div class="bg-[var(--el-color-primary)] w-1 h-5 rounded-full"></div>
+          <div class="text-lg font-medium text-[var(--el-text-color-primary)]">基础信息</div>
+        </div>
+        <el-form-item label="施工队伍" prop="deptName"
+          ><el-input v-model="form.deptName" disabled
+        /></el-form-item>
+        <el-form-item label="项目" prop="contractName"
+          ><el-input v-model="form.contractName" disabled
+        /></el-form-item>
+        <el-form-item label="任务" prop="taskName"
+          ><el-input v-model="form.taskName" disabled
+        /></el-form-item>
+        <el-form-item label="施工状态" prop="rigStatus">
+          <el-select
+            v-model="form.rigStatus"
+            placeholder="请选择施工状态"
+            :disabled="isMainFieldDisabled"
+          >
+            <el-option
+              v-for="(dict, index) in getStrDictOptions(DICT_TYPE.PMS_PROJECT_TASK_RY_SCHEDULE)"
+              :key="index"
+              :label="dict.label"
+              :value="dict.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="设计井深(m)" prop="designWellDepth">
+          <el-input v-model="form.designWellDepth" placeholder="" disabled />
+        </el-form-item>
+        <el-form-item label="当前井深(m)" prop="currentDepth">
+          <el-input-number
+            :min="0"
+            v-model="form.currentDepth"
+            placeholder="请输入当前井深(m)"
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+          />
+        </el-form-item>
+        <el-form-item label="当日用电量(kWh)" prop="dailyPowerUsage">
+          <el-input-number
+            :min="0"
+            v-model="form.dailyPowerUsage"
+            placeholder="请输入当日用电量(kWh)"
+            clearable
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+          />
+        </el-form-item>
+        <el-form-item label="当日油耗(升)" prop="dailyFuel">
+          <el-input-number
+            :min="0"
+            v-model="form.dailyFuel"
+            placeholder="请输入当日油耗(升)"
+            clearable
+            :class="{ 'warning-input': (form.dailyFuel ?? 0) > 9000 }"
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+          />
+        </el-form-item>
+        <el-form-item label="泥浆密度(g/cm³)" prop="mudDensity">
+          <el-input-number
+            :min="0"
+            v-model="form.mudDensity"
+            placeholder="请输入泥浆性能-密度(g/cm³)"
+            clearable
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+          />
+        </el-form-item>
+        <el-form-item label="泥浆粘度(S)" prop="mudViscosity">
+          <el-input-number
+            :min="0"
+            v-model="form.mudViscosity"
+            placeholder="请输入泥浆性能-粘度(S)"
+            clearable
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+          />
+        </el-form-item>
+        <el-form-item label="水平段长度(m)" prop="lateralLength">
+          <el-input-number
+            :min="0"
+            v-model="form.lateralLength"
+            placeholder="请输入水平段长度(m)"
+            clearable
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+          />
+        </el-form-item>
+        <el-form-item label="井斜(°)" prop="wellInclination">
+          <el-input-number
+            :min="0"
+            v-model="form.wellInclination"
+            placeholder="请输入井斜(°)"
+            clearable
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+          />
+        </el-form-item>
+        <el-form-item label="方位(°)" prop="azimuth">
+          <el-input-number
+            :min="0"
+            v-model="form.azimuth"
+            placeholder="请输入方位(°)"
+            clearable
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+          />
+        </el-form-item>
+        <el-form-item label="设计井身结构" prop="designWellStruct">
+          <el-input
+            v-model="form.designWellStruct"
+            placeholder=""
+            type="textarea"
+            disabled
+            autosize
+          />
+        </el-form-item>
+        <el-form-item label="人员情况" prop="personnel">
+          <el-input
+            v-model="form.personnel"
+            placeholder="请输入人员情况"
+            type="textarea"
+            :max-length="1000"
+            autosize
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+        <el-form-item class="col-span-2" label="生产动态" prop="productionStatus">
+          <el-input
+            v-model="form.productionStatus"
+            type="textarea"
+            autosize
+            maxlength="1000"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+        <el-form-item class="col-span-2" label="备注" prop="remark">
+          <el-input
+            v-model="form.remark"
+            type="textarea"
+            autosize
+            maxlength="1000"
+            :disabled="isMainFieldDisabled"
+          />
+        </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>
+          <div class="text-lg font-medium text-[var(--el-text-color-primary)]"
+            >生产与非生产时间</div
+          >
+        </div>
+
+        <el-form-item label="进尺工作时间(H)" prop="drillingWorkingTime">
+          <el-input-number
+            class="!w-full"
+            :min="0"
+            :max="24"
+            v-model="form.drillingWorkingTime"
+            :controls="false"
+            align="left"
+            placeholder="进尺+其他+非生产=24H"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+        <el-form-item label="其它生产时间(H)" prop="otherProductionTime">
+          <el-input-number
+            class="!w-full"
+            :min="0"
+            :max="24"
+            v-model="form.otherProductionTime"
+            :controls="false"
+            align="left"
+            placeholder="请输入其它生产时间(H)"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+
+        <!-- 循环渲染11个具体非生产时间 -->
+        <el-form-item
+          v-for="field in NON_PROD_FIELDS"
+          :key="field.key"
+          :label="field.label + '(H)'"
+          :prop="field.key"
+        >
+          <el-input-number
+            class="!w-full"
+            :min="0"
+            :max="24"
+            v-model="form[field.key as keyof FormOriginal]"
+            :controls="false"
+            align="left"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+
+        <!-- 其他非生产原因 -->
+        <el-form-item class="col-span-2" label="其他非生产原因" prop="otherNptReason">
+          <el-input
+            v-model="form.otherNptReason"
+            placeholder="请输入其他非生产原因"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+
+        <!-- 审批意见 -->
+        <div v-if="isApproval" class="col-span-2 mt-4 border-t pt-4">
+          <el-form-item label="审批意见" prop="opinion">
+            <el-input
+              v-model="form.opinion"
+              placeholder="请输入审批意见"
+              maxlength="1000"
+              type="textarea"
+              :autosize="{ minRows: 3 }"
+              :disabled="formType === 'readonly'"
+            />
+          </el-form-item>
+        </div>
+      </div>
+    </el-form>
+
+    <template #footer>
+      <div v-if="isEdit">
+        <el-button
+          type="primary"
+          @click="submitForm"
+          :loading="formLoading"
+          :disabled="formType === 'readonly'"
+          >确 定</el-button
+        >
+        <el-button @click="emits('update:visible', false)">取 消</el-button>
+      </div>
+      <div v-if="isApproval">
+        <el-button
+          type="primary"
+          @click="handleAudit(20)"
+          :loading="formLoading"
+          :disabled="formType === 'readonly'"
+          >审批通过</el-button
+        >
+        <el-button
+          type="danger"
+          @click="handleAudit(30)"
+          :loading="formLoading"
+          :disabled="formType === 'readonly'"
+          >审批拒绝</el-button
+        >
+        <el-button @click="emits('update:visible', false)">取 消</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<style scoped>
+: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;
+  }
+}
+</style>

+ 689 - 0
src/views/pms/iotrydailyreport/ry-xj-form.vue

@@ -0,0 +1,689 @@
+<script lang="ts" setup generic="T">
+import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { FormInstance, FormRules } from 'element-plus'
+import { computed, reactive, ref, watch, nextTick } from 'vue'
+
+interface Props {
+  visible: boolean
+  type?: 'edit' | 'approval' | 'readonly'
+  loadList: () => void
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  type: 'edit'
+})
+
+const emits = defineEmits(['update:visible'])
+
+// 1. 定义具体的 11 个非生产时间字段
+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: '其他非生产时间' }
+] as const
+
+interface FormOriginal {
+  id: number
+  deptId: number
+  projectId: number
+  taskId: number
+  deptName: string
+  contractName: string
+  taskName: string
+  repairStatus: string
+  technique: string
+  wellCategory: string
+  designWellDepth: string
+  wellControlLevel: string
+  casingPipeSize: string
+  dailyFuel: number
+  currentOperation: string
+  nextPlan: string
+  ratedProductionTime: number
+  productionTime: number
+  totalStaffNum: number
+  onDutyStaffNum: number
+  leaveStaffNum: number
+
+  // 11个非生产时间字段
+  repairTime: number
+  selfStopTime: number
+  accidentTime: number
+  complexityTime: number
+  rectificationTime: number
+  waitingStopTime: number
+  partyaDesign: number
+  partyaPrepare: number
+  partyaResource: number
+  relocationTime: number
+  winterBreakTime: number
+  otherNptTime: number
+
+  // 其他非生产时间原因(仅作为备注字段存在)
+  otherNptReason: string
+
+  productionStatus: string
+  remark: string
+  createTime: string
+  opinion: string
+}
+
+type Form = Partial<FormOriginal>
+
+// 字段白名单
+const FORM_KEYS: (keyof FormOriginal)[] = [
+  'id',
+  'deptId',
+  'projectId',
+  'taskId',
+  'deptName',
+  'contractName',
+  'taskName',
+  'repairStatus',
+  'technique',
+  'wellCategory',
+  'designWellDepth',
+  'wellControlLevel',
+  'casingPipeSize',
+  'dailyFuel',
+  'currentOperation',
+  'nextPlan',
+  'ratedProductionTime',
+  'productionTime',
+  'totalStaffNum',
+  'onDutyStaffNum',
+  'leaveStaffNum',
+  'productionStatus',
+  'remark',
+  'createTime',
+  'opinion',
+  'repairTime',
+  'selfStopTime',
+  'accidentTime',
+  'complexityTime',
+  'rectificationTime',
+  'waitingStopTime',
+  'partyaDesign',
+  'partyaPrepare',
+  'partyaResource',
+  'relocationTime',
+  'winterBreakTime',
+  'otherNptTime',
+  'otherNptReason'
+]
+
+const formRef = ref<FormInstance>()
+const loading = ref(false)
+const formLoading = ref(false)
+const formType = ref<'edit' | 'readonly'>('edit')
+const message = useMessage()
+const { t } = useI18n()
+
+// 初始化表单
+const initFormData = (): Form => {
+  const base: any = {
+    ratedProductionTime: 0,
+    productionTime: 0,
+    otherNptReason: '',
+    opinion: ''
+  }
+  // 初始化所有非生产时间字段为 0
+  NON_PROD_FIELDS.forEach((field) => {
+    base[field.key] = 0
+  })
+  return base as Form
+}
+
+const form = ref<Form>(initFormData())
+
+const isApproval = computed(() => props.type === 'approval')
+const isEdit = computed(() => props.type === 'edit')
+const isMainFieldDisabled = computed(() => formType.value === 'readonly' || isApproval.value)
+
+async function loadDetail(id: number) {
+  loading.value = true
+  try {
+    const res = await IotRyDailyReportApi.getIotRyDailyReport(id)
+    form.value = initFormData()
+    // 按需赋值
+    FORM_KEYS.forEach((key) => {
+      if (
+        Object.prototype.hasOwnProperty.call(res, key) &&
+        res[key] !== null &&
+        res[key] !== undefined
+      ) {
+        // @ts-ignore
+        form.value[key] = res[key]
+      }
+    })
+
+    if (props.type === 'edit' && res.status !== 0) formType.value = 'readonly'
+    if (props.type === 'approval' && res.auditStatus !== 10) formType.value = 'readonly'
+  } finally {
+    loading.value = false
+  }
+}
+
+function handleOpenForm(id: number, type: 'edit' | 'readonly') {
+  formType.value = type
+  emits('update:visible', true)
+  loadDetail(id).then(() => {
+    nextTick(() => formRef.value?.clearValidate())
+  })
+}
+
+defineExpose({ handleOpenForm })
+
+// --- 运行时效 ---
+const transitTime = computed(() => {
+  const cap = form.value.productionTime ?? 0
+  const gas = form.value.ratedProductionTime ?? 0
+
+  if (!gas) return { original: 0, value: '0%' }
+
+  const original = cap / gas
+  return { original, value: (original * 100).toFixed(2) + '%' }
+})
+// 在线人数 = 总人数 - 离职人数
+const onDutyStaffNum = computed(() => {
+  return (form.value.totalStaffNum ?? 0) - (form.value.leaveStaffNum ?? 0)
+})
+
+// --- 核心校验逻辑 ---
+
+// 计算所有非生产时间总和
+const sumNonProdTimes = () => {
+  let sum = 0
+  NON_PROD_FIELDS.forEach((field) => {
+    sum += (form.value[field.key as keyof FormOriginal] as number) || 0
+  })
+
+  return sum
+}
+
+// 24小时平衡校验器
+const validateTotalTime =
+  (isNon: boolean = false) =>
+  (_rule: any, _value: any, callback: any) => {
+    const rateTime = form.value.ratedProductionTime || 0
+    const time = form.value.productionTime || 0
+
+    const nonProdSum = sumNonProdTimes()
+
+    let total = 0
+    let msg = ''
+
+    total = parseFloat((time + nonProdSum).toFixed(2))
+    msg = `生产(${time})+非生产(${nonProdSum})=${total}H,必须等于额定${rateTime}H`
+
+    if (Math.abs(total - rateTime) > 0.01) {
+      if (!isNon) callback(new Error(msg))
+      else callback(new Error())
+    } else {
+      callback()
+    }
+  }
+
+const validateOtherReason = (_rule: any, value: any, callback: any) => {
+  const time = form.value.otherNptTime || 0
+  if (time > 0 && !value) {
+    callback(new Error('填写了其他时间,必须说明原因'))
+  } else {
+    callback()
+  }
+}
+
+// 动态构建校验规则
+const rules = reactive<FormRules>({
+  repairStatus: [{ required: true, message: '请选择施工状态', trigger: ['change', 'blur'] }],
+  productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['blur', 'change'] }],
+
+  // 生产时间绑定校验
+  ratedProductionTime: [
+    { required: true, message: '请输入额定生产时间', trigger: ['blur', 'change'] },
+    { validator: validateTotalTime(), trigger: ['blur', 'change'] }
+  ],
+  productionTime: [
+    { required: true, message: '请输入生产时间', trigger: ['blur', 'change'] },
+    { validator: validateTotalTime(), trigger: ['blur', 'change'] }
+  ],
+  otherProductionTime: [{ validator: validateTotalTime(), trigger: ['blur', 'change'] }],
+  otherNptReason: [{ validator: validateOtherReason, trigger: ['blur', 'change'] }]
+})
+
+// 关键步骤:为每一个非生产时间字段都绑定 validateTotalTime 规则
+// 这样当总和不对时,所有时间字段下面都会出现红色错误提示
+NON_PROD_FIELDS.forEach((field) => {
+  rules[field.key] = [{ validator: validateTotalTime(true), trigger: ['blur', 'change'] }]
+})
+
+// 监听所有时间字段
+const allTimeKeys = ['ratedProductionTime', 'productionTime', ...NON_PROD_FIELDS.map((f) => f.key)]
+
+// 当任一时间变化时,触发所有时间字段的校验更新
+watch(
+  () => allTimeKeys.map((key) => form.value[key as keyof FormOriginal]),
+  () => {
+    if (!isMainFieldDisabled.value) {
+      nextTick(() => {
+        // 传入数组,同时校验所有字段
+        formRef.value?.validateField(allTimeKeys)
+      })
+    }
+  },
+  { deep: true }
+)
+
+// --- 提交 ---
+const submitForm = async () => {
+  if (!formRef.value) return
+
+  try {
+    await formRef.value.validate()
+    formLoading.value = true
+    const submitData: any = {}
+    FORM_KEYS.forEach((key) => (submitData[key] = form.value[key]))
+    submitData.fillOrderCreateTime = form.value.createTime
+    submitData.projectClassification = '2'
+
+    await IotRyDailyReportApi.createIotRyDailyReport(submitData)
+    message.success(t('common.updateSuccess'))
+    emits('update:visible', false)
+    props.loadList()
+  } catch (error) {
+    console.warn('校验失败')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+const handleAudit = async (auditStatus: 20 | 30) => {
+  if (!formRef.value) return
+  try {
+    formLoading.value = true
+    await IotRyDailyReportApi.approvalIotRyDailyReport({
+      id: form.value.id!,
+      auditStatus,
+      opinion: form.value.opinion!
+    })
+    message.success(auditStatus === 20 ? '通过成功' : '拒绝成功')
+    emits('update:visible', false)
+    props.loadList()
+  } catch (error) {
+    console.warn(error)
+  } finally {
+    formLoading.value = false
+  }
+}
+
+const orange = computed(() => {
+  const rateTime = form.value.ratedProductionTime || 0
+  const time = form.value.productionTime || 0
+
+  const nonProdSum = sumNonProdTimes()
+
+  let total = 0
+
+  total = parseFloat((time + nonProdSum).toFixed(2))
+
+  if (Math.abs(total - rateTime) > 0.01) return true
+  return false
+})
+</script>
+
+<template>
+  <el-drawer
+    :model-value="visible"
+    @update:model-value="emits('update:visible', $event)"
+    header-class="mb-0!"
+    size="50%"
+  >
+    <template #header>
+      <span class="text-xl font-bold text-[var(--el-text-color-primary)]">
+        {{ type === 'edit' ? '编辑日报' : '审批日报' }}
+      </span>
+    </template>
+
+    <el-form
+      ref="formRef"
+      label-position="top"
+      size="default"
+      :rules="rules"
+      :model="form"
+      v-loading="loading"
+      require-asterisk-position="right"
+      :disabled="formType === 'readonly' && type !== 'approval'"
+    >
+      <!-- 顶部提示区 -->
+      <div class="flex flex-col gap-3 text-sm mb-4">
+        <div
+          class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
+        >
+          <div class="flex flex-col gap-2.5">
+            <div class="flex items-center justify-between">
+              <div class="text-gray-600 dark:text-gray-400">
+                <span class="font-bold text-gray-800 dark:text-gray-200">运行时效:</span>
+                生产时间/额定生产时间
+              </div>
+              <span
+                class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
+              >
+                >100% 红色预警
+              </span>
+            </div>
+            <div class="flex items-center justify-between">
+              <div class="text-gray-600 dark:text-gray-400">
+                <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
+                生产 + 非生产 = 额定生产
+              </div>
+              <span
+                class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
+              >
+                ≠额定生产 橙色预警
+              </span>
+            </div>
+            <div class="flex items-center justify-between">
+              <div class="text-gray-600 dark:text-gray-400">
+                <span class="font-bold text-gray-800 dark:text-gray-200"> 油量消耗:</span>
+                当日油耗
+              </div>
+              <span
+                class="inline-flex items-center rounded border border-blue-200 bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-600 dark:bg-blue-900/20 dark:border-blue-800 dark:text-blue-400"
+              >
+                >3500升 蓝色预警
+              </span>
+            </div>
+          </div>
+        </div>
+        <div
+          v-if="isEdit && form.opinion"
+          class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
+        >
+          <Icon
+            icon="ep:warning-filled"
+            class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
+          />
+          <div class="flex flex-col">
+            <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500">审核意见</h4>
+            <p class="leading-relaxed text-gray-600 dark:text-gray-400">{{ form.opinion }}</p>
+          </div>
+        </div>
+      </div>
+
+      <div class="grid grid-cols-2 gap-4">
+        <!-- 基础信息 -->
+        <div class="col-span-2 flex items-center gap-2 mt-2">
+          <div class="bg-[var(--el-color-primary)] w-1 h-5 rounded-full"></div>
+          <div class="text-lg font-medium text-[var(--el-text-color-primary)]">基础信息</div>
+        </div>
+        <el-form-item label="施工队伍" prop="deptName"
+          ><el-input v-model="form.deptName" disabled
+        /></el-form-item>
+        <el-form-item label="项目" prop="contractName"
+          ><el-input v-model="form.contractName" disabled
+        /></el-form-item>
+        <el-form-item label="任务" prop="taskName"
+          ><el-input v-model="form.taskName" disabled
+        /></el-form-item>
+        <el-form-item :label="t('project.status')" prop="repairStatus">
+          <el-select
+            v-model="form.repairStatus"
+            placeholder="请选择"
+            clearable
+            :disabled="isMainFieldDisabled"
+          >
+            <el-option
+              v-for="(dict, index) in getStrDictOptions(
+                DICT_TYPE.PMS_PROJECT_TASK_RY_REPAIR_SCHEDULE
+              )"
+              :key="index"
+              :label="dict.label"
+              :value="dict.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item :label="t('project.technology')" prop="technique">
+          <el-select v-model="form.technique" placeholder="请选择" disabled>
+            <el-option
+              v-for="(dict, index) in getStrDictOptions(DICT_TYPE.PMS_PROJECT_RY_TECHNOLOGY)"
+              :key="index"
+              :label="dict.label"
+              :value="dict.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="井别" prop="wellCategory">
+          <el-input v-model="form.wellCategory" placeholder="" disabled />
+        </el-form-item>
+        <el-form-item label="设计井深(m)" prop="designWellDepth">
+          <el-input v-model="form.designWellDepth" placeholder="" disabled />
+        </el-form-item>
+        <el-form-item label="井控级别" prop="wellControlLevel">
+          <el-input v-model="form.wellControlLevel" placeholder="" disabled />
+        </el-form-item>
+        <el-form-item label="套生段产管尺寸(mm)" prop="casingPipeSize">
+          <el-input v-model="form.casingPipeSize" placeholder="" disabled />
+        </el-form-item>
+        <el-form-item label="当日油耗(升)" prop="dailyFuel">
+          <el-input-number
+            class="w-full!"
+            :min="0"
+            v-model="form.dailyFuel"
+            :class="{ 'blue-input': (form.dailyFuel ?? 0) > 3500 }"
+            placeholder="请输入当日油耗(升)"
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+          />
+        </el-form-item>
+        <el-form-item :label="t('project.currentOperation')" prop="currentOperation">
+          <el-input
+            v-model="form.currentOperation"
+            placeholder="请输入目前工序"
+            type="textarea"
+            autosize
+            :maxlength="1000"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+        <el-form-item :label="t('project.nextPlan')" prop="nextPlan">
+          <el-input
+            v-model="form.nextPlan"
+            placeholder="请输入下步工序"
+            type="textarea"
+            autosize
+            :maxlength="1000"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+        <el-form-item label="运行时效" prop="transitTime">
+          <el-input
+            :model-value="transitTime.value"
+            placeholder="运行时效"
+            disabled
+            :class="{ 'warning-input': transitTime.original >= 1.0 }"
+            id="transitTimeInput"
+          />
+        </el-form-item>
+        <el-form-item label="全员数量" prop="totalStaffNum">
+          <el-input-number
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+            :min="0"
+            v-model="form.totalStaffNum"
+            placeholder="请输入全员数量"
+          />
+        </el-form-item>
+        <el-form-item label="在岗人数" prop="onDutyStaffNum">
+          <el-input-number :min="0" v-model="onDutyStaffNum" placeholder="" disabled />
+        </el-form-item>
+        <el-form-item label="休假人员数量" prop="leaveStaffNum">
+          <el-input-number
+            :disabled="isMainFieldDisabled"
+            :controls="false"
+            align="left"
+            :min="0"
+            v-model="form.leaveStaffNum"
+            placeholder="请输入休假人员数量"
+          />
+        </el-form-item>
+        <el-form-item class="col-span-2" label="生产动态" prop="productionStatus">
+          <el-input
+            v-model="form.productionStatus"
+            type="textarea"
+            autosize
+            maxlength="1000"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+        <el-form-item class="col-span-2" label="备注" prop="remark">
+          <el-input
+            v-model="form.remark"
+            type="textarea"
+            autosize
+            maxlength="1000"
+            :disabled="isMainFieldDisabled"
+          />
+        </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>
+          <div class="text-lg font-medium text-[var(--el-text-color-primary)]"
+            >生产与非生产时间</div
+          >
+        </div>
+
+        <el-form-item label="额定生产时间(H)" prop="ratedProductionTime">
+          <el-input-number
+            class="!w-full"
+            :min="0"
+            v-model="form.ratedProductionTime"
+            :controls="false"
+            align="left"
+            placeholder="请输入额定生产时间(H)"
+            :disabled="isMainFieldDisabled"
+            :class="{ 'orange-input': orange }"
+          />
+        </el-form-item>
+        <el-form-item label="生产时间(H)" prop="productionTime">
+          <el-input-number
+            class="!w-full"
+            :min="0"
+            v-model="form.productionTime"
+            :controls="false"
+            align="left"
+            placeholder="请输入生产时间(H)"
+            :disabled="isMainFieldDisabled"
+            :class="{ 'orange-input': orange }"
+          />
+        </el-form-item>
+
+        <!-- 循环渲染11个具体非生产时间 -->
+        <el-form-item
+          v-for="field in NON_PROD_FIELDS"
+          :key="field.key"
+          :label="field.label + '(H)'"
+          :prop="field.key"
+        >
+          <el-input-number
+            class="!w-full"
+            :min="0"
+            v-model="form[field.key as keyof FormOriginal]"
+            :controls="false"
+            align="left"
+            :disabled="isMainFieldDisabled"
+            :class="{ 'orange-input': orange }"
+          />
+        </el-form-item>
+
+        <!-- 其他非生产原因 -->
+        <el-form-item class="col-span-2" label="其他非生产原因" prop="otherNptReason">
+          <el-input
+            v-model="form.otherNptReason"
+            placeholder="请输入其他非生产原因"
+            :disabled="isMainFieldDisabled"
+          />
+        </el-form-item>
+
+        <!-- 审批意见 -->
+        <div v-if="isApproval" class="col-span-2 mt-4 border-t pt-4">
+          <el-form-item label="审批意见" prop="opinion">
+            <el-input
+              v-model="form.opinion"
+              placeholder="请输入审批意见"
+              maxlength="1000"
+              type="textarea"
+              :autosize="{ minRows: 3 }"
+              :disabled="formType === 'readonly'"
+            />
+          </el-form-item>
+        </div>
+      </div>
+    </el-form>
+
+    <template #footer>
+      <div v-if="isEdit">
+        <el-button
+          type="primary"
+          @click="submitForm"
+          :loading="formLoading"
+          :disabled="formType === 'readonly'"
+          >确 定</el-button
+        >
+        <el-button @click="emits('update:visible', false)">取 消</el-button>
+      </div>
+      <div v-if="isApproval">
+        <el-button
+          type="primary"
+          @click="handleAudit(20)"
+          :loading="formLoading"
+          :disabled="formType === 'readonly'"
+          >审批通过</el-button
+        >
+        <el-button
+          type="danger"
+          @click="handleAudit(30)"
+          :loading="formLoading"
+          :disabled="formType === 'readonly'"
+          >审批拒绝</el-button
+        >
+        <el-button @click="emits('update:visible', false)">取 消</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<style scoped>
+: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;
+  }
+}
+</style>

+ 12 - 431
src/views/pms/iotrydailyreport/xapproval.vue

@@ -2,12 +2,11 @@
 import { rangeShortcuts } from '@/utils/formatTime'
 import { useDebounceFn } from '@vueuse/core'
 import dayjs from 'dayjs'
-import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { DICT_TYPE } from '@/utils/dict'
 import { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults'
-import { FormInstance, FormRules } from 'element-plus'
-import Form from '@/components/Form/src/Form.vue'
 import { useUserStore } from '@/store/modules/user'
 import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import ryXjForm from './ry-xj-form.vue'
 
 interface List {
   id: number
@@ -433,178 +432,23 @@ watch(
   { immediate: true }
 )
 
-const FORM_KEYS = [
-  'id',
-  'deptId',
-  'projectId',
-  'taskId',
-  'deptName',
-  'contractName',
-  'taskName',
-  'repairStatus',
-  'technique',
-  'wellCategory',
-  'designWellDepth',
-  'casingPipeSize',
-  'wellControlLevel',
-  'currentOperation',
-  'nextPlan',
-  'transitTime',
-  'ratedProductionTime',
-  'productionTime',
-  'nonProductionTime',
-  'ryNptReason',
-  'productionStatus',
-  'totalStaffNum',
-  'onDutyStaffNum',
-  'leaveStaffNum',
-  'remark',
-  'opinion',
-  'createTime',
-  'dailyFuel'
-] as const
-
-type FormKey = (typeof FORM_KEYS)[number]
-type Form = Partial<Pick<List, FormKey>>
-
-const dialogVisible = ref(false)
-const formRef = ref<FormInstance>()
-const formLoading = ref(false)
-const message = useMessage()
-
-const initFormData = (): Form => ({})
-
-const form = ref<Form>(initFormData())
-
-async function loadDetail(id: number) {
-  try {
-    const res = await IotRyDailyReportApi.getIotRyDailyReport(id)
-    FORM_KEYS.forEach((key) => {
-      form.value[key] = res[key] ?? form.value[key]
-    })
-    form.value.id = id
-
-    if (res.auditStatus !== 10) {
-      formType.value = 'readonly'
-    }
-  } finally {
-  }
-}
+const visible = ref(false)
 
-const formType = ref<'approval' | 'readonly'>('approval')
+const formRef = ref()
 
-function handleOpenForm(id: number, type: 'approval' | 'readonly') {
-  form.value = initFormData()
-  formRef.value?.resetFields()
-
-  formType.value = type
-
-  dialogVisible.value = true
-  loadDetail(id).then(() => {
-    formRef.value?.validate()
-  })
+function handleOpenForm(id: number, type: 'edit' | 'readonly') {
+  if (formRef.value) {
+    formRef.value.handleOpenForm(id, type)
+  }
 }
 
 const route = useRoute()
 
 onMounted(() => {
   if (Object.keys(route.query).length > 0) {
-    handleOpenForm(Number(route.query.id), 'approval')
-  }
-})
-
-const transitTime = computed(() => {
-  const cap = form.value.productionTime ?? 0
-  const gas = form.value.ratedProductionTime ?? 0
-
-  if (!gas) return { original: 0, value: '0%' }
-
-  const original = cap / gas
-  return { original, value: (original * 100).toFixed(2) + '%' }
-})
-
-const onDutyStaffNum = computed(() => {
-  return (form.value.totalStaffNum ?? 0) - (form.value.leaveStaffNum ?? 0)
-})
-
-const sumTimes = () => {
-  const { productionTime = 0, nonProductionTime = 0 } = form.value
-  return productionTime + nonProductionTime
-}
-
-const validateTotalTime = (_rule: any, _value: any, callback: any) => {
-  const total = sumTimes()
-  if (total !== form.value.ratedProductionTime) {
-    callback(new Error(`生产时间和非生产时间之和必须等于额定生产时间`))
-  } else {
-    callback()
-  }
-}
-
-const validateNptReason = (_rule: any, value: any, callback: any) => {
-  if ((form.value.nonProductionTime || 0) > 0 && !value) {
-    callback(new Error('非生产时间大于 0 时,必须选择原因'))
-  } else {
-    callback()
+    handleOpenForm(Number(route.query.id), 'edit')
   }
-}
-
-const timeRuleItem = [
-  { required: true, message: '请输入时间', trigger: 'blur' },
-  { validator: validateTotalTime, trigger: 'blur' }
-]
-
-const rules = reactive<FormRules>({
-  repairStatus: [{ required: true, message: '请输入施工状态', trigger: ['change', 'blur'] }],
-  productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['change', 'blur'] }],
-
-  // 复用规则
-  productionTime: timeRuleItem,
-  nonProductionTime: timeRuleItem,
-  ratedProductionTime: timeRuleItem,
-
-  ryNptReason: [{ validator: validateNptReason, trigger: ['change', 'blur'] }]
 })
-
-watch(
-  [
-    () => form.value.productionTime,
-    () => form.value.nonProductionTime,
-    () => form.value.ratedProductionTime
-  ],
-  () => {
-    nextTick(() => {
-      formRef.value?.validateField('nptReason')
-      if (sumTimes() === form.value.ratedProductionTime) {
-        formRef.value?.clearValidate(['productionTime', 'nonProductionTime', 'ratedProductionTime'])
-      }
-    })
-  }
-)
-
-const { t } = useI18n()
-
-const submitForm = async (auditStatus: 20 | 30) => {
-  if (!formRef.value) return
-
-  try {
-    // await formRef.value.validate()
-    formLoading.value = true
-    const { opinion, id } = form.value
-
-    const data = { id: id, auditStatus, opinion } as any
-
-    await IotRyDailyReportApi.approvalIotRyDailyReport(data)
-    message.success(auditStatus === 20 ? '通过成功' : '拒绝成功')
-    dialogVisible.value = false
-
-    loadList()
-  } catch (error) {
-    console.warn('表单校验未通过或提交出错')
-  } finally {
-    formLoading.value = false
-  }
-}
 </script>
 
 <template>
@@ -689,7 +533,7 @@ const submitForm = async (auditStatus: 20 | 30) => {
                         v-show="row.auditStatus === 10"
                         link
                         type="primary"
-                        @click="handleOpenForm(row.id, 'approval')"
+                        @click="handleOpenForm(row.id, 'edit')"
                         v-hasPermi="['pms:iot-ry-daily-report:update']"
                       >
                         审批
@@ -717,257 +561,8 @@ const submitForm = async (auditStatus: 20 | 30) => {
         </div>
       </div>
     </div>
-    <Dialog title="编辑" v-model="dialogVisible">
-      <el-form
-        ref="formRef"
-        label-position="top"
-        size="default"
-        :rules="rules"
-        :model="form"
-        v-loading="formLoading"
-        require-asterisk-position="right"
-        :disabled="formType === 'readonly'"
-      >
-        <div class="flex flex-col gap-3 text-sm">
-          <div
-            class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
-          >
-            <div class="flex flex-col gap-2.5">
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">运行时效:</span>
-                  生产时间/额定生产时间
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
-                >
-                  >100% 红色预警
-                </span>
-              </div>
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
-                  生产 + 非生产 = 额定生产
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
-                >
-                  ≠额定生产 橙色预警
-                </span>
-              </div>
-            </div>
-          </div>
-          <!-- <div
-          v-if="form.opinion"
-          class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
-        >
-          <Icon
-            icon="ep:warning-filled"
-            class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
-          />
-          <div class="flex flex-col">
-            <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500"> 审核意见 </h4>
-            <p class="leading-relaxed text-gray-600 dark:text-gray-400">
-              {{ form.opinion }}
-            </p>
-          </div>
-        </div> -->
-        </div>
-        <div class="grid grid-cols-2 gap-4 mt-5">
-          <el-form-item label="施工队伍" prop="deptName">
-            <el-input v-model="form.deptName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="项目" prop="contractName">
-            <el-input v-model="form.contractName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="任务" prop="taskName">
-            <el-input v-model="form.taskName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item :label="t('project.status')" prop="repairStatus">
-            <el-select v-model="form.repairStatus" placeholder="请选择" clearable disabled>
-              <el-option
-                v-for="(dict, index) in getStrDictOptions(
-                  DICT_TYPE.PMS_PROJECT_TASK_RY_REPAIR_SCHEDULE
-                )"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item :label="t('project.technology')" prop="technique">
-            <el-select v-model="form.technique" placeholder="请选择" disabled>
-              <el-option
-                v-for="(dict, index) in getStrDictOptions(DICT_TYPE.PMS_PROJECT_RY_TECHNOLOGY)"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="井别" prop="wellCategory">
-            <el-input v-model="form.wellCategory" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="设计井深(m)" prop="designWellDepth">
-            <el-input v-model="form.designWellDepth" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="井控级别" prop="wellControlLevel">
-            <el-input v-model="form.wellControlLevel" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="套生段产管尺寸(mm)" prop="casingPipeSize">
-            <el-input v-model="form.casingPipeSize" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="当日油耗(升)" prop="dailyFuel">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyFuel"
-              placeholder="请输入当日油耗(升)"
-            />
-          </el-form-item>
-          <el-form-item :label="t('project.currentOperation')" prop="currentOperation">
-            <el-input
-              v-model="form.currentOperation"
-              placeholder="请输入目前工序"
-              type="textarea"
-              autosize
-              :maxlength="1000"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item :label="t('project.nextPlan')" prop="nextPlan">
-            <el-input
-              v-model="form.nextPlan"
-              placeholder="请输入下步工序"
-              type="textarea"
-              autosize
-              :maxlength="1000"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="运行时效" prop="transitTime">
-            <el-input
-              :model-value="transitTime.value"
-              placeholder="运行时效"
-              disabled
-              :class="{ 'warning-input': transitTime.original >= 1.0 }"
-              id="transitTimeInput"
-            />
-          </el-form-item>
-          <el-form-item label="额定生产时间(H)" prop="ratedProductionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.ratedProductionTime"
-              placeholder="请输入额定生产时间(H)"
-              :class="{ 'orange-input': sumTimes() !== Number(form.ratedProductionTime ?? 0) }"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="生产时间(H)" prop="productionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.productionTime"
-              placeholder="请输入生产时间(H)"
-              :class="{ 'orange-input': sumTimes() !== Number(form.ratedProductionTime ?? 0) }"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="非生产时间(H)" prop="nonProductionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.nonProductionTime"
-              placeholder="非生产时间(H)"
-              :class="{ 'orange-input': sumTimes() !== Number(form.ratedProductionTime ?? 0) }"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="非生产时间原因" prop="nptReason">
-            <el-select v-model="form.ryNptReason" placeholder="请选择" clearable disabled>
-              <el-option
-                v-for="(dict, index) of getStrDictOptions(DICT_TYPE.PMS_PROJECT_RY_NPT_REASON)"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="全员数量" prop="totalStaffNum">
-            <el-input-number
-              :min="0"
-              v-model="form.totalStaffNum"
-              placeholder="请输入全员数量"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="在岗人数" prop="onDutyStaffNum">
-            <el-input-number :min="0" v-model="onDutyStaffNum" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="休假人员数量" prop="leaveStaffNum">
-            <el-input-number
-              :min="0"
-              v-model="form.leaveStaffNum"
-              placeholder="请输入休假人员数量"
-              disabled
-            />
-          </el-form-item>
-        </div>
-        <div class="grid grid-cols-1 gap-4 mt-5">
-          <el-form-item label="生产动态" prop="productionStatus">
-            <el-input
-              v-model="form.productionStatus"
-              placeholder="请输入生产动态"
-              type="textarea"
-              autosize
-              :max-length="1000"
-              disabled
-            />
-          </el-form-item>
-          <el-form-item label="备注" prop="remark">
-            <el-input
-              v-model="form.remark"
-              placeholder="请输入备注"
-              :max-length="1000"
-              type="textarea"
-              autosize
-              disabled
-            />
-          </el-form-item>
-        </div>
-        <el-form-item class="mt-4" label="审批意见" prop="opinion">
-          <el-input
-            v-model="form.opinion"
-            placeholder="请输入审批意见"
-            :max-length="1000"
-            type="textarea"
-            autosize
-            :disabled="formType === 'readonly'"
-          />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button
-          size="default"
-          @click="submitForm(20)"
-          type="primary"
-          :disabled="formLoading || formType === 'readonly'"
-        >
-          审批通过
-        </el-button>
-        <el-button
-          size="default"
-          @click="submitForm(30)"
-          type="danger"
-          :disabled="formLoading || formType === 'readonly'"
-        >
-          审批拒绝
-        </el-button>
-        <el-button size="default" @click="dialogVisible = false">取 消</el-button>
-      </template>
-    </Dialog></div
-  >
+    <ry-xj-form v-model:visible="visible" type="approval" ref="formRef" :load-list="loadList" />
+  </div>
 </template>
 
 <style scoped>
@@ -990,20 +585,6 @@ const submitForm = async (auditStatus: 20 | 30) => {
   }
 }
 
-:deep(.warning-input) {
-  .el-input__inner {
-    color: red !important;
-    -webkit-text-fill-color: red !important;
-  }
-}
-
-:deep(.orange-input) {
-  .el-input__inner {
-    color: orange !important;
-    -webkit-text-fill-color: orange !important;
-  }
-}
-
 :deep(.el-input-number) {
   width: 100%;
 }

+ 9 - 404
src/views/pms/iotrydailyreport/xfill.vue

@@ -2,12 +2,11 @@
 import { rangeShortcuts } from '@/utils/formatTime'
 import { useDebounceFn } from '@vueuse/core'
 import dayjs from 'dayjs'
-import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { DICT_TYPE } from '@/utils/dict'
 import { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults'
-import { FormInstance, FormRules } from 'element-plus'
-import Form from '@/components/Form/src/Form.vue'
 import { useUserStore } from '@/store/modules/user'
 import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import ryXjForm from './ry-xj-form.vue'
 
 interface List {
   id: number
@@ -433,76 +432,14 @@ watch(
   { immediate: true }
 )
 
-const FORM_KEYS = [
-  'id',
-  'deptId',
-  'projectId',
-  'taskId',
-  'deptName',
-  'contractName',
-  'taskName',
-  'repairStatus',
-  'technique',
-  'wellCategory',
-  'designWellDepth',
-  'casingPipeSize',
-  'wellControlLevel',
-  'currentOperation',
-  'nextPlan',
-  'transitTime',
-  'ratedProductionTime',
-  'productionTime',
-  'nonProductionTime',
-  'ryNptReason',
-  'productionStatus',
-  'totalStaffNum',
-  'onDutyStaffNum',
-  'leaveStaffNum',
-  'remark',
-  'opinion',
-  'createTime',
-  'dailyFuel'
-] as const
-
-type FormKey = (typeof FORM_KEYS)[number]
-type Form = Partial<Pick<List, FormKey>>
-
-const dialogVisible = ref(false)
-const formRef = ref<FormInstance>()
-const formLoading = ref(false)
-const message = useMessage()
-
-const initFormData = (): Form => ({})
-
-const form = ref<Form>(initFormData())
-
-async function loadDetail(id: number) {
-  try {
-    const res = await IotRyDailyReportApi.getIotRyDailyReport(id)
-    FORM_KEYS.forEach((key) => {
-      form.value[key] = res[key] ?? form.value[key]
-    })
-    form.value.id = id
-
-    if (res.status !== 0) {
-      formType.value = 'readonly'
-    }
-  } finally {
-  }
-}
+const visible = ref(false)
 
-const formType = ref<'edit' | 'readonly'>('edit')
+const formRef = ref()
 
 function handleOpenForm(id: number, type: 'edit' | 'readonly') {
-  form.value = initFormData()
-  formRef.value?.resetFields()
-
-  formType.value = type
-
-  dialogVisible.value = true
-  loadDetail(id).then(() => {
-    formRef.value?.validate()
-  })
+  if (formRef.value) {
+    formRef.value.handleOpenForm(id, type)
+  }
 }
 
 const route = useRoute()
@@ -512,103 +449,6 @@ onMounted(() => {
     handleOpenForm(Number(route.query.id), 'edit')
   }
 })
-
-const transitTime = computed(() => {
-  const cap = form.value.productionTime ?? 0
-  const gas = form.value.ratedProductionTime ?? 0
-
-  if (!gas) return { original: 0, value: '0%' }
-
-  const original = cap / gas
-  return { original, value: (original * 100).toFixed(2) + '%' }
-})
-
-const onDutyStaffNum = computed(() => {
-  return (form.value.totalStaffNum ?? 0) - (form.value.leaveStaffNum ?? 0)
-})
-
-const sumTimes = () => {
-  const { productionTime = 0, nonProductionTime = 0 } = form.value
-  return productionTime + nonProductionTime
-}
-
-const validateTotalTime = (_rule: any, _value: any, callback: any) => {
-  const total = sumTimes()
-  if (total !== form.value.ratedProductionTime) {
-    callback(new Error(`生产时间和非生产时间之和必须等于额定生产时间`))
-  } else {
-    callback()
-  }
-}
-
-const validateNptReason = (_rule: any, value: any, callback: any) => {
-  if ((form.value.nonProductionTime || 0) > 0 && !value) {
-    callback(new Error('非生产时间大于 0 时,必须选择原因'))
-  } else {
-    callback()
-  }
-}
-
-const _timeRuleItem = [
-  { required: true, message: '请输入时间', trigger: 'blur' },
-  { validator: validateTotalTime, trigger: 'blur' }
-]
-
-const rules = reactive<FormRules>({
-  repairStatus: [{ required: true, message: '请输入施工状态', trigger: ['change', 'blur'] }],
-  productionStatus: [{ required: true, message: '请输入生产动态', trigger: ['change', 'blur'] }],
-
-  // 复用规则
-  // productionTime: timeRuleItem,
-  // nonProductionTime: timeRuleItem,
-  // ratedProductionTime: timeRuleItem,
-
-  ryNptReason: [{ validator: validateNptReason, trigger: ['change', 'blur'] }]
-})
-
-watch(
-  [
-    () => form.value.productionTime,
-    () => form.value.nonProductionTime,
-    () => form.value.ratedProductionTime
-  ],
-  () => {
-    nextTick(() => {
-      formRef.value?.validateField('nptReason')
-      if (sumTimes() === form.value.ratedProductionTime) {
-        formRef.value?.clearValidate(['productionTime', 'nonProductionTime', 'ratedProductionTime'])
-      }
-    })
-  }
-)
-
-const { t } = useI18n()
-
-const submitForm = async () => {
-  if (!formRef.value) return
-
-  try {
-    await formRef.value.validate()
-    formLoading.value = true
-
-    const total = sumTimes()
-    if (total !== form.value.ratedProductionTime) {
-      message.error(`生产时间和非生产时间之和必须等于额定生产时间`)
-    }
-
-    const { createTime, ...other } = form.value
-
-    const data = { ...other, fillOrderCreateTime: createTime, projectClassification: '2' } as any
-    await IotRyDailyReportApi.createIotRyDailyReport(data)
-    message.success(t('common.updateSuccess'))
-    dialogVisible.value = false
-    loadList()
-  } catch (error) {
-    console.warn('表单校验未通过或提交出错')
-  } finally {
-    formLoading.value = false
-  }
-}
 </script>
 
 <template>
@@ -721,229 +561,8 @@ const submitForm = async () => {
         </div>
       </div>
     </div>
-    <Dialog title="编辑" v-model="dialogVisible">
-      <el-form
-        ref="formRef"
-        label-position="top"
-        size="default"
-        :rules="rules"
-        :model="form"
-        v-loading="formLoading"
-        require-asterisk-position="right"
-        :disabled="formType === 'readonly'"
-      >
-        <div class="flex flex-col gap-3 text-sm">
-          <div
-            class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
-          >
-            <div class="flex flex-col gap-2.5">
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">运行时效:</span>
-                  生产时间/额定生产时间
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
-                >
-                  >100% 红色预警
-                </span>
-              </div>
-              <div class="flex items-center justify-between">
-                <div class="text-gray-600 dark:text-gray-400">
-                  <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
-                  生产 + 非生产 = 额定生产
-                </div>
-                <span
-                  class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
-                >
-                  ≠额定生产 橙色预警
-                </span>
-              </div>
-            </div>
-          </div>
-          <div
-            v-if="form.opinion"
-            class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
-          >
-            <Icon
-              icon="ep:warning-filled"
-              class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
-            />
-            <div class="flex flex-col">
-              <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500"> 审核意见 </h4>
-              <p class="leading-relaxed text-gray-600 dark:text-gray-400">
-                {{ form.opinion }}
-              </p>
-            </div>
-          </div>
-        </div>
-        <div class="grid grid-cols-2 gap-4 mt-5">
-          <el-form-item label="施工队伍" prop="deptName">
-            <el-input v-model="form.deptName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="项目" prop="contractName">
-            <el-input v-model="form.contractName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="任务" prop="taskName">
-            <el-input v-model="form.taskName" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item :label="t('project.status')" prop="repairStatus">
-            <el-select v-model="form.repairStatus" placeholder="请选择" clearable>
-              <el-option
-                v-for="(dict, index) in getStrDictOptions(
-                  DICT_TYPE.PMS_PROJECT_TASK_RY_REPAIR_SCHEDULE
-                )"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item :label="t('project.technology')" prop="technique">
-            <el-select v-model="form.technique" placeholder="请选择" disabled>
-              <el-option
-                v-for="(dict, index) in getStrDictOptions(DICT_TYPE.PMS_PROJECT_RY_TECHNOLOGY)"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <!-- <el-form-item label="运行时效" prop="transitTime">
-          <el-input
-            :model-value="transitTime.value"
-            placeholder="运行时效"
-            disabled
-            :class="{ 'warning-input': transitTime.original > 1.0 }"
-            id="transitTimeInput"
-          />
-        </el-form-item> -->
-          <el-form-item label="井别" prop="wellCategory">
-            <el-input v-model="form.wellCategory" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="设计井深(m)" prop="designWellDepth">
-            <el-input v-model="form.designWellDepth" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="井控级别" prop="wellControlLevel">
-            <el-input v-model="form.wellControlLevel" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="套生段产管尺寸(mm)" prop="casingPipeSize">
-            <el-input v-model="form.casingPipeSize" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="当日油耗(升)" prop="dailyFuel">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.dailyFuel"
-              placeholder="请输入当日油耗(升)"
-            />
-          </el-form-item>
-          <el-form-item :label="t('project.currentOperation')" prop="currentOperation">
-            <el-input
-              v-model="form.currentOperation"
-              placeholder="请输入目前工序"
-              type="textarea"
-              autosize
-              :maxlength="1000"
-            />
-          </el-form-item>
-          <el-form-item :label="t('project.nextPlan')" prop="nextPlan">
-            <el-input
-              v-model="form.nextPlan"
-              placeholder="请输入下步工序"
-              type="textarea"
-              autosize
-              :maxlength="1000"
-            />
-          </el-form-item>
-          <el-form-item label="运行时效" prop="transitTime">
-            <el-input
-              :model-value="transitTime.value"
-              placeholder="运行时效"
-              disabled
-              :class="{ 'warning-input': transitTime.original >= 1.0 }"
-              id="transitTimeInput"
-            />
-          </el-form-item>
-          <el-form-item label="额定生产时间(H)" prop="ratedProductionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.ratedProductionTime"
-              :class="{ 'orange-input': sumTimes() !== Number(form.ratedProductionTime ?? 0) }"
-              placeholder="请输入额定生产时间(H)"
-            />
-          </el-form-item>
-          <el-form-item label="生产时间(H)" prop="productionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.productionTime"
-              :class="{ 'orange-input': sumTimes() !== Number(form.ratedProductionTime ?? 0) }"
-              placeholder="请输入生产时间(H)"
-            />
-          </el-form-item>
-          <el-form-item label="非生产时间(H)" prop="nonProductionTime">
-            <el-input-number
-              class="w-full!"
-              :min="0"
-              v-model="form.nonProductionTime"
-              :class="{ 'orange-input': sumTimes() !== Number(form.ratedProductionTime ?? 0) }"
-              placeholder="非生产时间(H)"
-            />
-          </el-form-item>
-          <el-form-item label="非生产时间原因" prop="nptReason">
-            <el-select v-model="form.ryNptReason" placeholder="请选择" clearable>
-              <el-option
-                v-for="(dict, index) of getStrDictOptions(DICT_TYPE.PMS_PROJECT_RY_NPT_REASON)"
-                :key="index"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="全员数量" prop="totalStaffNum">
-            <el-input-number :min="0" v-model="form.totalStaffNum" placeholder="请输入全员数量" />
-          </el-form-item>
-          <el-form-item label="在岗人数" prop="onDutyStaffNum">
-            <el-input-number :min="0" v-model="onDutyStaffNum" placeholder="" disabled />
-          </el-form-item>
-          <el-form-item label="休假人员数量" prop="leaveStaffNum">
-            <el-input-number
-              :min="0"
-              v-model="form.leaveStaffNum"
-              placeholder="请输入休假人员数量"
-            />
-          </el-form-item>
-        </div>
-        <div class="grid grid-cols-1 gap-4 mt-5">
-          <el-form-item label="生产动态" prop="productionStatus">
-            <el-input
-              v-model="form.productionStatus"
-              placeholder="请输入生产动态"
-              type="textarea"
-              autosize
-              :max-length="1000"
-            />
-          </el-form-item>
-          <el-form-item label="备注" prop="remark">
-            <el-input
-              v-model="form.remark"
-              placeholder="请输入备注"
-              :max-length="1000"
-              type="textarea"
-              autosize
-            /> </el-form-item
-        ></div>
-      </el-form>
-      <template #footer>
-        <el-button size="default" @click="submitForm" type="primary" :disabled="formLoading">
-          确 定
-        </el-button>
-        <el-button size="default" @click="dialogVisible = false">取 消</el-button>
-      </template>
-    </Dialog></div
-  >
+    <ry-xj-form v-model:visible="visible" type="edit" ref="formRef" :load-list="loadList" />
+  </div>
 </template>
 
 <style scoped>
@@ -966,20 +585,6 @@ const submitForm = async () => {
   }
 }
 
-:deep(.warning-input) {
-  .el-input__inner {
-    color: red !important;
-    -webkit-text-fill-color: red !important;
-  }
-}
-
-:deep(.orange-input) {
-  .el-input__inner {
-    color: orange !important;
-    -webkit-text-fill-color: orange !important;
-  }
-}
-
 :deep(.el-input-number) {
   width: 100%;
 }