Forráskód Böngészése

✨ feat(瑞都日报): 添加当日油耗

Zimo 1 hete
szülő
commit
1e40370570

+ 147 - 8
pages/ruiDu/compontents/report-form.vue

@@ -1,6 +1,7 @@
 <script setup>
   import { useDataDictStore } from '@/store/modules/dataDict';
   import { onMounted, reactive, ref, computed, getCurrentInstance, watch } from 'vue';
+  import { useDebounceFn } from '@/utils/useDebounceFn.js';
 
   import tpfTimeRange from '@/components/tpf-time-range/tpf-time-range.vue';
   import deviceTransfer from '@/components/device-transfer/index.vue';
@@ -88,6 +89,8 @@
     form.endTime = data[1];
   };
 
+  const dailyFuel = ref(0);
+
   const form = reactive({
     startTime: startDefaultTime.value,
     endTime: endDefaultTime.value,
@@ -130,6 +133,14 @@
         },
       ],
     },
+    nextPlan: {
+      rules: [
+        {
+          required: true,
+          errorMessage: `请输入下步工作计划`,
+        },
+      ],
+    },
   });
 
   const validate = async () => {
@@ -145,6 +156,14 @@
     // // 处理表单数据
     const formDataCopy = JSON.parse(JSON.stringify(form));
 
+    if (!formDataCopy.dailyFuel && formDataCopy.dailyFuel !== 0) {
+      uni.showToast({
+        title: '请输入当日油耗',
+        icon: 'none',
+      });
+      return;
+    }
+
     const responseData = [];
     // // 处理施工工艺
     form.platformIds.forEach(id => {
@@ -194,6 +213,7 @@
           customFuel: Number(item.customFuel),
           reportId: id,
         })),
+        dailyFuel: Number(formDataCopy.dailyFuel),
       };
 
       responseData.push(data);
@@ -240,6 +260,8 @@
     handleEquipmentNames(selectedIds);
   };
 
+  const steps = ref([]);
+
   const formDataFormat = () => {
     // 处理时间范围
     timeRangeFormat();
@@ -286,13 +308,16 @@
     // 附件
     form.attachments = props.reportData.attachments || [];
 
-    form.reportFuels = (props.reportData.reportFuels || []).map(v => ({
-      ...v,
-      customFuel: Number(Number(v.zhbdFuel ?? 0).toFixed(2)),
-    }));
+    form.reportFuels = ((props.formDisable ? props.reportData.reportedFuels : props.reportData.reportFuels) || []).map(
+      v => ({
+        ...v,
+        customFuel: Number(Number(v.zhbdFuel ?? 0).toFixed(2)),
+      })
+    );
 
-    form.dailyFuel = form.reportFuels.reduce((prev, cur) => prev + Number(cur.customFuel), 0);
+    steps.value = (props.reportData.taskProgresses ?? []).map(v => ({ title: v.rdStatusLabel, desc: v.createTime }));
 
+    initDailyFuel();
     // form.
     // 展示用的文件列表
     // attachmentsFileList.value =
@@ -627,10 +652,114 @@
   defineExpose({
     submitForm,
   });
+
+  // 假设你已经定义了 props
+  // const props = defineProps({ reportData: Object });
+
+  // --- 1. 公共工具函数 (保持不变) ---
+  const parseNumber = val => {
+    let num = parseFloat(val);
+    if (isNaN(num)) num = 0;
+    if (num < 0) num = 0;
+    return Number(num.toFixed(2));
+  };
+
+  // --- 2. 防抖逻辑定义 (保持不变) ---
+
+  // 列表变化 -> 算总和
+  const handleListChange = useDebounceFn(() => {
+    let total = 0;
+    form.reportFuels.forEach(item => {
+      const formattedVal = parseNumber(item.customFuel);
+      if (item.customFuel !== formattedVal) {
+        item.customFuel = formattedVal;
+      }
+      total += formattedVal;
+    });
+    // 更新 dailyFuel,这会触发下面的 dailyFuel watcher
+    dailyFuel.value = parseNumber(total);
+  }, 500);
+
+  // dailyFuel 变化 -> 格式化自身 & 同步 form
+  const handleDailyFuelChange = useDebounceFn(() => {
+    const formattedVal = parseNumber(dailyFuel.value);
+
+    if (dailyFuel.value !== formattedVal) {
+      dailyFuel.value = formattedVal;
+    }
+    form.dailyFuel = formattedVal;
+  }, 500);
+
+  // --- 3. 关键修改:初始化逻辑 ---
+
+  const initDailyFuel = () => {
+    // 获取 props 中的值 (转为数字以做判断)
+    const propVal = props.reportData?.dailyFuel;
+    const numPropVal = parseFloat(propVal);
+
+    // 判断规则:如果有值且不是 NaN (根据需求,你也可以加上 > 0 的判断)
+    // 这里假设只要 props 里有有效数字,就以 props 为准
+    const hasPropValue = !isNaN(numPropVal) && propVal !== null && propVal !== '';
+
+    if (hasPropValue) {
+      // 【情况A】Props 有值:直接使用 Props
+      const val = parseNumber(numPropVal);
+      dailyFuel.value = val;
+      form.dailyFuel = val;
+      // // 顺便把列表里的每一项也格式化一下(可选)
+      // form.reportFuels.forEach(item => {
+      //   item.customFuel = parseNumber(item.customFuel);
+      // });
+    } else {
+      // 【情况B】Props 没值:根据列表计算初始值
+      // 这里我们不使用防抖,直接立即计算一次,确保显示正确
+      let total = 0;
+      form.reportFuels.forEach(item => {
+        // 初始化时顺便把列表里的脏数据格式化了
+        const val = parseNumber(item.customFuel);
+        item.customFuel = val;
+        total += val;
+      });
+      const finalTotal = parseNumber(total);
+      dailyFuel.value = finalTotal;
+      form.dailyFuel = finalTotal;
+    }
+  };
+
+  // 执行初始化
+  initDailyFuel();
+
+  // --- 4. 监听器 (关键修改) ---
+
+  // 监听列表:【注意】这里去掉了 immediate: true
+  // 因为初始化我们已经在上面手动 initDailyFuel() 里做过了
+  // 现在只监听用户后续的“修改”操作
+  watch(
+    () => form.reportFuels,
+    () => {
+      handleListChange();
+    },
+    { deep: true } // 只有 deep,没有 immediate
+  );
+
+  // 监听 dailyFuel
+  watch(
+    () => dailyFuel.value,
+    (newVal, oldVal) => {
+      // 只有当值真的变了,才触发防抖更新
+      // 避免初始化赋值时触发不必要的逻辑
+      if (newVal !== oldVal) {
+        handleDailyFuelChange();
+      }
+    }
+  );
 </script>
 
 <template>
   <scroll-view scroll-y="true" class="report-form">
+    <scroll-view class="steps" scroll-x :scroll-y="false">
+      <uni-steps :options="steps" :active="steps.length - 1" :style="{ width: `${steps.length * 100}px` }" />
+    </scroll-view>
     <uni-forms
       ref="reportFormRef"
       labelWidth="140px"
@@ -702,7 +831,7 @@
             :disabled="true"
             v-model="unselectedEquipmentNames" />
         </uni-forms-item>
-        <uni-forms-item class="form-item" label="当日油耗(L):">
+        <uni-forms-item :required="isRequired" class="form-item" label="当日油耗(L):">
           <uni-easyinput
             class="digit-item"
             type="number"
@@ -711,7 +840,7 @@
             :styles="{ disableColor: '#fff' }"
             :placeholder="inputPlaceholder"
             :disabled="formDisable"
-            v-model="form.dailyFuel" />
+            v-model="dailyFuel" />
         </uni-forms-item>
 
         <uni-forms-item
@@ -732,7 +861,11 @@
             :maxlength="1000" />
         </uni-forms-item>
         <!-- 下步工作计划 -->
-        <uni-forms-item class="form-item" :label="`${$t('ruiDu.nextWorkPlan')}:`" name="nextPlan">
+        <uni-forms-item
+          class="form-item"
+          :required="isRequired"
+          :label="`${$t('ruiDu.nextWorkPlan')}:`"
+          name="nextPlan">
           <uni-easyinput
             style="text-align: right"
             type="textarea"
@@ -986,6 +1119,12 @@
     color: #333;
   }
 
+  .steps {
+    :deep(.uni-scroll-view > .uni-scroll-view) {
+      padding: 10px 0 20px 0;
+    }
+  }
+
   :deep(.uni-textarea-textarea:disabled),
   :deep(.uni-input-input:disabled) {
     color: #333;

+ 1 - 1
pages/ruihen/components/form.vue

@@ -71,7 +71,7 @@
       }
 
       if (!form.value.capacity) {
-        uni.showToast({ title: '请维护增压机产能' });
+        uni.showToast({ title: '请维护增压机产能', icon: 'none' });
       }
     } finally {
     }

+ 41 - 0
utils/useDebounceFn.js

@@ -0,0 +1,41 @@
+import { onUnload } from '@dcloudio/uni-app';
+
+/**
+ * 自定义防抖函数钩子
+ * @param {Function} fn 需要防抖执行的函数
+ * @param {Number} delay 延迟时间(毫秒)
+ * @returns {Function} 经过防抖处理的函数
+ */
+export function useDebounceFn(fn, delay = 200) {
+  let timer = null;
+
+  const debounced = (...args) => {
+    // 如果已有定时器,清除它
+    if (timer) clearTimeout(timer);
+
+    // 设置新的定时器
+    timer = setTimeout(() => {
+      fn.apply(this, args);
+    }, delay);
+  };
+
+  // 挂载一个 cancel 方法,以便在外部需要时手动清除
+  debounced.cancel = () => {
+    if (timer) {
+      clearTimeout(timer);
+      timer = null;
+    }
+  };
+
+  // 在组件卸载时自动清理定时器,避免内存泄漏
+  // try-catch 是为了防止在组件外部(非setup环境)使用时报错
+  try {
+    onUnload(() => {
+      debounced.cancel();
+    });
+  } catch (e) {
+    // 如果不是在组件 setup 中调用,忽略 onUnmounted 错误
+  }
+
+  return debounced;
+}