Explorar el Código

运行记录填报优化

zhouql hace 2 semanas
padre
commit
6a5906dfe6
Se han modificado 4 ficheros con 989 adiciones y 183 borrados
  1. 17 0
      api/recordFilling.js
  2. 793 0
      pages/recordFilling/detail-old.vue
  3. 178 182
      pages/recordFilling/detail.vue
  4. 1 1
      request/index.js

+ 17 - 0
api/recordFilling.js

@@ -80,4 +80,21 @@ export function recordFillingUpOperationOrder(data) {
 		method: 'POST',
 		data: data
 	})
+}
+
+// 获取运行记录填报工单详情-设备列表+属性
+export function recordFillingDetailGetPageAndAttrs(params) {
+	return request({
+		url: '/rq/iot-opeation-fill/orderFillpage1',
+		method: 'GET',
+		params
+	});
+}
+// 保存运行记录填报工单详情填报内容
+export function recordFillingDetailInsertDataList(data) {
+	return request({
+		url: '/rq/iot-opeation-fill/insertDataList',
+		method: 'POST',
+		data: data
+	})
 }

+ 793 - 0
pages/recordFilling/detail-old.vue

@@ -0,0 +1,793 @@
+<template>
+  <z-paging
+    class="page"
+    ref="paging"
+    v-model="dataList"
+    :loading-more-enabled="false"
+    @query="queryList"
+  >
+    <!-- z-paging默认铺满全屏,此时页面所有view都应放在z-paging标签内,否则会被盖住 -->
+    <!-- 需要固定在页面顶部的view请通过slot="top"插入,包括自定义的导航栏 -->
+    <view class="list">
+      <!-- 工单基础信息 -->
+      <view class="item top">
+        <view class="item-content flex-row align-center">
+          <view class="item-title full-cell flex-row align-center">
+            <span class="item-title-width"
+              >{{ $t("operationRecordFilling.workOrderName") }}:</span
+            >
+            <span>{{ params.orderName }}</span>
+          </view>
+        </view>
+        <view class="item-content flex-row align-center">
+          <view class="item-title full-cell flex-row align-center">
+            <span class="item-title-width"
+              >{{ $t("operationRecordFilling.responsiblePerson") }}:</span
+            >
+            <span>{{ params.userName }}</span>
+          </view>
+        </view>
+        <view class="item-content flex-row align-center">
+          <view class="item-title full-cell flex-row align-center">
+            <span class="item-title-width"
+              >{{ $t("operation.createTime") }}:</span
+            >
+            <span>{{ params.createTime }}</span>
+          </view>
+        </view>
+      </view>
+      <!-- 填报列表 -->
+      <view class="item" v-for="(item, index) in dataList" :key="index">
+        <view class="item-module flex-row align-center justify-between">
+          <view class="module-name">
+            {{ item.deviceCode }}({{ item.deviceName }})
+          </view>
+          <view class="module-border"> </view>
+        </view>
+        <view class="item-content flex-row align-center justify-between bold">
+          <view class="item-title flex-row align-center">
+            <span>{{ $t("operationRecordFilling.belongToTeam") }}:</span>
+            <span>{{ item.orgName }}</span>
+          </view>
+        </view>
+        <view
+          class="item-content flex-row align-center justify-between bold"
+          v-for="sum in item.sumList"
+        >
+          <view class="item-title flex-row align-center word-break-all">
+            <span>{{ sum.name }}:</span>
+          </view>
+          <view class="item-value flex-row align-center justify-end total">
+            <uni-easyinput
+              style="text-align: right"
+              :inputBorder="false"
+              :clearable="true"
+              :styles="{ disableColor: '#fff' }"
+              :value="`${sum.totalRunTime} ${
+                sum.modelAttr ? (sum.modelAttr.includes('Time') ? 'h' : '') : ''
+              }`"
+              :disabled="true"
+            ></uni-easyinput>
+          </view>
+        </view>
+        <view
+          class="item-content flex-col align-center justify-between"
+          :class="{ 'bottom-bold': item.nonSumList.length > 0 }"
+          v-for="nosum in item.nonSumList"
+        >
+          <!-- isCollection为1,提示:以下数值取自PLC,如有不符请修改 -->
+          <uni-notice-bar
+            :text="$t('operationRecordFilling.plcNotice')"
+            v-if="nosum.isCollection == 1"
+          />
+          <view class="flex-row align-center justify-between item-content">
+            <view class="item-title flex-row align-center">
+              <span>{{ nosum.name }}:</span>
+            </view>
+            <!-- 判断填写项的属性 -->
+            <!-- type为double时,输入框为数字类型 -->
+            <view
+              class="item-value flex-row align-center justify-end"
+              v-if="nosum.type == 'double'"
+            >
+              <uni-easyinput
+                style="text-align: right"
+                :styles="{ disableColor: '#fff' }"
+                :inputBorder="false"
+                :clearable="true"
+                :placeholder="$t('operation.PleaseFillIn')"
+                :disabled="!isView"
+                v-model="nosum.fillContent"
+                :type="'digit'"
+                @blur="
+                  nosum.threshold > 0
+                    ? checkThreshold(nosum)
+                    : checkLessThreshold(nosum)
+                "
+                @change="handleFillContentChange(nosum, item)"
+              ></uni-easyinput>
+            </view>
+            <!-- type为textarea时,输入框为文本类型 -->
+            <view
+              class="item-value flex-row align-center justify-end"
+              v-else-if="nosum.type == 'textarea'"
+            >
+              <uni-easyinput
+                style="text-align: right"
+                :styles="{ disableColor: '#fff' }"
+                :inputBorder="false"
+                :clearable="true"
+                :placeholder="$t('operation.PleaseFillIn')"
+                :disabled="!isView"
+                v-model="nosum.fillContent"
+                :type="'textarea'"
+                :autoHeight="true"
+                :maxlength="-1"
+              ></uni-easyinput>
+            </view>
+            <!-- type为enum时,使用下拉菜单 -->
+            <view
+              class="item-value textarea flex-row align-center justify-end"
+              v-else-if="nosum.type == 'enum' && nosum.description !== null"
+            >
+              <uni-data-select
+                :localdata="nosum.enumList"
+                :styles="{ disableColor: '#fff' }"
+                :clear="false"
+                :disabled="!isView"
+                :placeholder="$t('operation.PleaseSelect')"
+                v-model="nosum.fillContent"
+              ></uni-data-select>
+            </view>
+            <!-- 其他类型时,输入框为文本类型 -->
+            <view class="item-value flex-row align-center justify-end" v-else>
+              <uni-easyinput
+                style="text-align: right"
+                :styles="{ disableColor: '#fff' }"
+                :inputBorder="false"
+                :clearable="true"
+                :placeholder="$t('operation.PleaseFillIn')"
+                :disabled="!isView"
+                v-model="nosum.fillContent"
+                :type="'text'"
+              ></uni-easyinput>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <!-- 如果需要使用页脚,请使用slot="bottom"slot节点不支持通过v-if或v-show动态显示/隐藏,若需要动态控制,可将v-if添加在其子节点上 -->
+    <template #bottom>
+      <button
+        style="border-radius: 0"
+        type="primary"
+        @click="onSubmit()"
+      >
+        {{ $t("operation.save") }}
+      </button>
+    </template>
+  </z-paging>
+</template>
+
+<script setup>
+import { ref, reactive, getCurrentInstance, watch, onMounted } from "vue";
+import { onReady, onLoad } from "@dcloudio/uni-app";
+import dayjs from "dayjs";
+import {
+  getRecordFillingDetailGetPage,
+  getRecordFillingDetailGetAttrs,
+  recordFillingDetailInsertLog,
+  getRecordFillingDetail,
+  recordFillingUpOperationOrder,
+} from "@/api/recordFilling";
+import { getUserId, reloginByUserId } from "@/utils/auth.js";
+import { useDataDictStore } from "@/store/modules/dataDict";
+// 引用全局变量$t
+const { appContext } = getCurrentInstance();
+const t = appContext.config.globalProperties.$t;
+
+// 获取字典项
+const { getStrDictOptions, getIntDictOptions } = useDataDictStore();
+
+// -------------------------------------
+const isFromMsg = ref(false);
+const params = ref({});
+const isView = ref(false); // 是否编辑 -- view == 1为编辑状态
+
+onMounted(() => {
+  console.log("onMounted");
+});
+
+onReady(() => {
+  console.log("onReady");
+});
+
+onLoad(async (option) => {
+  console.log("onLoad", option);
+  await reloginByUserId(option.reloginUserId);
+  isFromMsg.value = !!option.reloginUserId;
+  // 初始化params
+  params.value = JSON.parse(option.param);
+  // 处理createTime
+  params.value.createTime = params.value.createTime
+    ? dayjs(Number.parseInt(params.value.createTime)).format("YYYY-MM-DD")
+    : "";
+  // 请求工单详情
+  if (params.value?.orderId) {
+    const detail = (await getRecordFillingDetail(params.value.orderId)).data;
+    console.log("🚀getRecordFillingDetail ~ detail:", detail);
+    params.value = {
+      ...params.value,
+      ...detail,
+      // 处理createTime
+      createTime: detail.createTime
+        ? dayjs(Number.parseInt(detail.createTime)).format("YYYY-MM-DD")
+        : "",
+      orderId: detail.id,
+    };
+  }
+
+  console.log("🚀 ~ params.value:", params.value);
+  // 处理是否可编辑 {0: '待填写', 1: '已完成', 2: '填写中', 3: '忽略'}
+  isView.value = params.value?.orderStatus % 2 == 0;
+  console.log("🚀 ~ isView.value:", isView.value);
+});
+const paging = ref(null);
+// v-model绑定的这个变量不要在分页请求结束中自己赋值,直接使用即可
+const dataList = ref([]);
+
+// 监听dataList变化,初始化时计算一次总和
+watch(
+  dataList,
+  (newVal) => {
+    // calculateTotalRunTime();
+  },
+  { deep: true }
+);
+
+// 处理fillContent变化的方法
+const handleFillContentChange = (nosum, deviceItem) => {
+  console.log("🚀 ~ nosum, deviceItem:", nosum, deviceItem);
+  // 处理增压机
+  if (
+    deviceItem.deviceName.includes("增压机") &&
+    nosum.name === "当日运转时间"
+  ) {
+    calculateTotalRunTime("增压机", "当日运转时间"); // 计算当日运转时间总和
+  }
+  // 处理提纯撬
+  if (deviceItem.deviceName.includes("提纯撬") && nosum.name === "当日注气量") {
+    calculateTotalRunTime("提纯撬", "当日注气量"); // 计算当日注气量总和
+  }
+  // 处理注水泵
+  if (deviceItem.deviceName.includes("注水泵") && nosum.name === "当日注水量") {
+    calculateTotalRunTime("注水泵", "当日注水量"); // 计算当日注水量总和
+  }
+  // 处理箱式变电站
+  if (
+    deviceItem.deviceName.includes("箱式变电站") &&
+    nosum.name === "当日用电量"
+  ) {
+    calculateTotalRunTime("箱式变电站", "当日用电量"); // 计算当日用电量总和
+  }
+};
+
+/**
+ * 计算所有deviceName中包含deviceNameToMatch的对象中对应的reportName的fillContent总和并更新到reportName的fillContent中
+ * @param deviceNameToMatch {string} 设备名称包含的字符串
+ * @param reportName {string} 填写项名称
+ */
+const calculateTotalRunTime = (deviceNameToMatch, reportName) => {
+  console.log(
+    "🚀calculateTotalRunTime ~ deviceNameToMatch, reportName:",
+    deviceNameToMatch,
+    12
+  );
+  // 查找isReport为1的对象
+  const reportItem = dataList.value.find((item) => item.isReport === 1);
+  console.log("🚀calculateTotalRunTime ~ reportItem:", reportItem);
+
+  if (!reportItem) return;
+  /**
+   * @param deviceNameToMatch {string} 设备名称包含的字符串
+   * @param deviceName {string} 设备名称
+   * @param reportName {string} 填写项名称
+   * 查找[生产日报]中对应的填写项
+   * reportName -> deviceName:reportName
+   * 当日运转时间 -> 增压机:当日运转时间
+   * 当日注气量 -> 提纯撬:当日注气量
+   * 当日注水量 -> 注水泵:当日注水量
+   * 当日用电量 -> 箱式变电站:当日用电量
+   */
+  const targetItem = reportItem.nonSumList.find(
+    (item) => item.name === reportName
+  );
+
+  if (!targetItem) return;
+
+  // 计算所有deviceName中包含deviceNameToMatch的对象中对应的reportName的fillContent总和
+  let total = null;
+  dataList.value.forEach((item) => {
+    if (item.deviceName.includes(deviceNameToMatch) && item.nonSumList) {
+      item.nonSumList.forEach((nonSum) => {
+        // 只累加数字类型的值
+        if (
+          nonSum.type === "double" &&
+          nonSum.fillContent &&
+          nonSum.name === reportName
+        ) {
+          console.log("🚀 ~ nonSum.fillContent:", nonSum.fillContent);
+          console.log("🚀 ~ nonSum:", nonSum);
+          const value = Number(nonSum.fillContent) || 0;
+          total += value;
+        }
+      });
+    }
+  });
+  console.log("🚀 ~ total:", total);
+  if (total !== null) {
+    // 更新目标值,保留两位小数
+    targetItem.fillContent = toFixed(total);
+    console.log("🚀 ~ targetItem.fillContent:", targetItem.fillContent);
+  }
+};
+
+// @query所绑定的方法不要自己调用!!需要刷新列表数据时,只需要调用paging.value.reload()即可
+const queryList = (pageNo, pageSize) => {
+  const userId = uni.getStorageSync("userId");
+  if (!userId) {
+    paging.value.complete([]);
+    return;
+  }
+
+  // 此处请求仅为演示,请替换为自己项目中的请求
+  getRecordFillingDetailGetPage({
+    // pageNo,
+    // pageSize,
+    ...params.value,
+    deviceCategoryId: 1,
+  })
+    .then(async (res) => {
+      const resList = [].concat(res.data);
+
+      // 使用Promise.all等待所有异步请求完成
+      await Promise.all(
+        resList.map(async (item) => {
+          try {
+            const attrParams = {
+              deviceCode: item.deviceCode,
+              deviceName: item.deviceName,
+              deptId: item.deptId,
+              createTime: params.value.createTime,
+              deviceCategoryId: item.deviceCategoryId,
+              deviceId: item.deviceId,
+              userId: params.value.userId,
+              orderId: params.value.orderId,
+            };
+            // console.log(
+            //   "getRecordFillingDetailGetAttrs- attrParams",
+            //   attrParams
+            // );
+            const resAttrs = await getRecordFillingDetailGetAttrs({
+              pageNo: 1,
+              pageSize: 10,
+              ...attrParams,
+            });
+            // console.log("resAttrs", resAttrs);
+            if (resAttrs?.data) {
+              attrParams.createTime = attrParams.createTime
+                ? dayjs(attrParams.createTime).format("YYYY-MM-DD")
+                : "";
+              attrParams.id = attrParams.orderId;
+              delete attrParams.orderId;
+              delete attrParams.deviceName;
+              resAttrs.data.map((rtem) => {
+                // 将rtem中sumList和nonSumList两个数组中的
+                // fillContent字段判断是否为null, 如果为null,则赋值为0 不为null则保留两位小数
+                // 将attrParams合并到两个数组的每个对象中
+                // 然后将sumList和nonSumList分别赋值给item的sumList和nonSumList
+
+                if (rtem.sumList) {
+                  rtem.sumList.map((sumItem) => {
+                    if (
+                      sumItem.fillContent == null ||
+                      sumItem.fillContent == ""
+                    ) {
+                      // console.log("🚀 ~ rtem.sumList.map ~ sumItem:", sumItem);
+                      // sumItem.fillContent = 0;
+                    } else {
+                      // 如果是double类型,保留两位小数
+                      if (sumItem.type == "double") {
+                        sumItem.fillContent = toFixed(sumItem.fillContent);
+                      }
+                    }
+                    // 将sumItem的id赋值给modelId
+                    sumItem.modelId = sumItem.id;
+
+                    sumItem.pointName = sumItem.name;
+                    // 合并attrParams到sumItem中
+                    sumItem = Object.assign(sumItem, attrParams);
+                  });
+                }
+                if (rtem.nonSumList) {
+                  //
+                  rtem.nonSumList.map((nonSumItem) => {
+                    if (
+                      nonSumItem.fillContent == null ||
+                      nonSumItem.fillContent == ""
+                    ) {
+                      // console.log(
+                      //   "🚀 ~ rtem.nonSumList.map ~ nonSumItem:",
+                      //   nonSumItem
+                      // );
+                      // nonSumItem.fillContent = 0;
+                    } else {
+                      // 如果是double类型,保留两位小数
+                      if (nonSumItem.type == "double") {
+                        nonSumItem.fillContent = toFixed(
+                          nonSumItem.fillContent
+                        );
+                      }
+                    }
+                    nonSumItem.pointName = nonSumItem.name;
+                    // 将nonSumItem的id赋值给modelId
+                    nonSumItem.modelId = nonSumItem.id;
+                    // 合并attrParams到nonSumItem中
+                    nonSumItem = Object.assign(nonSumItem, attrParams);
+                    // 如果是enum类型,且description不为null,则根据description获取对应字典项数组,赋值给enumList
+                    if (nonSumItem.type == "enum" && nonSumItem.description) {
+                      console.log("🚀 ~ onSumItem.description:");
+                      nonSumItem.enumList =
+                        nonSumItem.name === "非生产原因"
+                          ? getIntDictOptions(nonSumItem.description).map(
+                              (dict) => {
+                                return {
+                                  ...dict,
+                                  text: dict.label,
+                                };
+                              }
+                            )
+                          : getStrDictOptions(nonSumItem.description).map(
+                              (dict) => {
+                                return {
+                                  ...dict,
+                                  text: dict.label,
+                                };
+                              }
+                            );
+                      console.log(
+                        "🚀 ~  nonSumItem.enumList:",
+                        nonSumItem.enumList
+                      );
+                    }
+                  });
+                }
+                item.sumList = rtem.sumList;
+
+                item.nonSumList = rtem.nonSumList;
+              });
+              console.log("resAttrs-modelId", resAttrs);
+            }
+          } catch (error) {
+            console.error("获取属性失败", error);
+            // 可以选择设置默认值或标记错误状态
+            item.sumList = [];
+            item.nonSumList = [];
+          }
+        })
+      );
+      console.log("resList--", resList);
+      // 将请求结果通过complete传给z-paging处理,同时也代表请求结束,这一行必须调用
+      paging.value.complete(resList);
+    })
+    .catch((res) => {
+      // 如果请求失败写paging.value.complete(false);
+      // 注意,每次都需要在catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
+      // 在底层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
+      paging.value.complete(false);
+    });
+};
+// 判断是否小于阈值 (<0)
+const checkLessThreshold = (item) => {
+  if (item.fillContent < 0) {
+    uni.showToast({
+      title:
+        item.name +
+        t("operationRecordFilling.fillContentCannotLessThanThreshold") +
+        "0",
+      icon: "none",
+    });
+    item.fillContent = ""; // 清空输入
+    return false; // 返回false表示校验失败
+  }
+};
+// 判断是否大于阈值
+const checkThreshold = (item) => {
+  checkLessThreshold(item);
+  // 如果threshold > 0,则判断fillContent是否大于threshold,如果大于则提示用户填写小于等于threshold的值
+  if (item.fillContent > item.threshold) {
+    uni.showToast({
+      title:
+        item.name +
+        t("operationRecordFilling.fillContentCannotGreaterThanThreshold") +
+        item.threshold,
+      icon: "none",
+    });
+    item.fillContent = ""; // 清空输入
+    return false; // 返回false表示校验失败
+  }
+};
+
+// 保留两位小数
+const toFixed = (num) => {
+  if (num) {
+    num = Number(num);
+    num = num.toFixed(2);
+  } else {
+    num = 0.0;
+  }
+  return num;
+};
+
+const onSubmit = async () => {
+  console.log("onSubmit", dataList.value);
+  // 1. 校验所有必填项
+  // 遍历dataList.value中nonSumList每个item(非生产日报 isReport!=1)的fillContent字段,
+  // 如果为null或者为空,则提示用户填写,
+  // 如果threshold > 0,则判断fillContent是否大于threshold,如果大于则提示用户填写小于等于threshold的值
+  // 如果所有项全部填写,则调用填写记录接口
+
+  for (const item of dataList.value) {
+    const nonSumList = item.nonSumList;
+    for (const nonSumItem of nonSumList) {
+      if (
+        (!item.isReport || item.isReport != 1) &&
+        (nonSumItem.fillContent == null || nonSumItem.fillContent === "")
+      ) {
+        uni.showToast({
+          title:
+            t("operation.PleaseFillIn") +
+            item.deviceCode +
+            "(" +
+            item.deviceName +
+            ")" +
+            t("operation.allItem"),
+          icon: "none",
+        });
+        return; // 校验失败直接返回
+      }
+      if (nonSumItem.fillContent != "" && nonSumItem.fillContent != null) {
+        console.log("🚀 ~ nonSumItem:", nonSumItem);
+        console.log("🚀 ~ nonSumItem.fillContent:", nonSumItem.fillContent);
+        // 先将值转换为字符串进行操作
+        const fillContentStr = String(nonSumItem.fillContent);
+        //  将字符串转换为数字
+        const num = Number(fillContentStr);
+
+        // 检查转换后的数字是否有效
+        if (!isNaN(num)) {
+          // 检查是否包含小数(使用字符串检查)
+          if (fillContentStr.includes(".")) {
+            // 保留两位小数(假设toFixed是你定义的保留两位小数的函数)
+            nonSumItem.fillContent = toFixed(num);
+          } else {
+            // 转换为整数
+            nonSumItem.fillContent = Math.floor(num);
+          }
+        }
+      }
+      // 如果threshold > 0,则判断fillContent是否大于threshold
+      if (nonSumItem.threshold > 0) {
+        if (nonSumItem.fillContent > nonSumItem.threshold) {
+          uni.showToast({
+            title:
+              item.deviceCode +
+              "(" +
+              item.deviceName +
+              ")" +
+              nonSumItem.name +
+              t(
+                "operationRecordFilling.fillContentCannotGreaterThanThreshold"
+              ) +
+              nonSumItem.threshold,
+            icon: "none",
+            duration: 3000,
+          });
+          nonSumItem.fillContent = ""; // 清空输入
+          return; // 校验失败直接返回
+        }
+      }
+    }
+  }
+  // 定义新的dataList副本 用于提交数据,避免修改原数据
+  const subDataList = JSON.parse(JSON.stringify(dataList.value));
+
+  // 3. 处理副本:删除 enumList(仅修改副本,不影响原数据)
+  for (const item of subDataList) {
+    // 先判断 item.nonSumList 存在,避免空指针
+    if (item.nonSumList && item.nonSumList.length) {
+      for (const nonSumItem of item.nonSumList) {
+        if (nonSumItem.enumList) {
+          delete nonSumItem.enumList;
+        }
+      }
+    }
+  }
+  console.log("提交用的副本数据:subDataList", subDataList);
+
+  try {
+    // 2. 收集所有保存请求(不在这里处理导航)
+    const submitPromises = subDataList.map(async (item) => {
+      const submitData = [].concat(item.sumList).concat(item.nonSumList);
+      // 仅返回接口结果,不执行导航
+      return await recordFillingDetailInsertLog(submitData);
+    });
+
+    // 3. 等待所有请求完成
+    const results = await Promise.all(submitPromises);
+
+    // 4. 所有请求完成后,统一判断结果
+    const allSuccess = results.every((res) => res?.code === 0);
+    if (allSuccess) {
+      // 5. 调用更新工单状态接口
+      const upRes = await recordFillingUpOperationOrder({
+        id: params.value.orderId,
+      });
+      console.log("🚀 ~ upRes:", upRes)
+      if (upRes?.code === 0) {
+        console.log("工单状态更新成功");
+        uni.showToast({
+          title: t("operation.success"),
+          duration: 1500,
+          icon: "none",
+        });
+        setTimeout(() => {
+          uni.navigateBack();
+        }, 1500);
+      } else {
+        console.error("工单状态更新失败", upRes);
+        uni.showToast({
+          title: t("operation.fail"),
+          icon: "none",
+        });
+      }
+    } else {
+      uni.showToast({
+        title: t("operation.fail"),
+        icon: "none",
+      });
+    }
+  } catch (error) {
+    console.error("保存失败", error);
+    uni.showToast({
+      title: t("operation.fail"),
+      icon: "error",
+    });
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.page {
+  padding: 0;
+  box-sizing: border-box;
+}
+
+.top {
+  padding: 10px;
+}
+
+.list {
+  // margin-top: calc(10px);
+  padding: 10px;
+  height: calc(100%);
+}
+
+.item {
+  width: 100%;
+  // height: 204px;
+  background: #ffffff;
+  border-radius: 6px;
+  margin-bottom: 10px;
+  box-sizing: border-box;
+  padding: 20px 15px;
+}
+
+.item-module {
+  width: 100%;
+  height: 16px;
+  position: relative;
+  font-weight: 600;
+  font-size: 14px;
+  color: #333333;
+  margin-bottom: 10px;
+
+  .module-border {
+    position: absolute;
+    left: -15px;
+    width: 0px;
+    height: 12px;
+    border: 1px solid #004098;
+  }
+}
+
+.item-content {
+  position: relative;
+  width: 100%;
+  // height: calc(38px);
+  box-sizing: border-box;
+  font-weight: 500;
+  font-size: 14px;
+  color: #333333;
+  line-height: 20px;
+  border-bottom: 1px dashed #cacccf;
+
+  &:last-child {
+    border-bottom: none;
+  }
+
+  &.bold {
+    font-weight: 600;
+    // :deep(.uni-easyinput__content-input){
+    // 	padding-right: 0 !important;
+    // }
+  }
+  &.bottom-bold {
+    border-bottom: 1px dashed #cacccf;
+  }
+}
+
+.item-title {
+  position: relative;
+  min-height: 38px;
+  width: 55%;
+
+  &.total {
+    :deep(.is-disabled) {
+      color: #333333 !important;
+    }
+  }
+  &.full-cell {
+    width: 100%;
+    min-width: max-content;
+  }
+}
+
+.item-value {
+  width: 45%;
+  position: relative;
+  &.textarea {
+    width: 65%;
+  }
+}
+.word-break-all {
+  min-width: unset;
+}
+:deep(.uni-select) {
+  border: none;
+  text-align: right;
+  padding-right: 0;
+  .uniui-bottom:before {
+    content: "\e6b5" !important;
+    font-size: 16px !important;
+  }
+}
+:deep(.uni-select--disabled) {
+  color: #d5d5d5 !important;
+  background-color: transparent;
+  .uni-select__input-text {
+    color: #d5d5d5 !important;
+  }
+}
+:deep(.uni-select__selector) {
+  text-align: left;
+}
+:deep(.uni-select__selector-item) {
+  border-bottom: 1px dashed #cacccf;
+  text-align: left;
+}
+:deep(.uni-easyinput__content-textarea) {
+  min-height: inherit;
+  margin: 10px;
+}
+</style>

+ 178 - 182
pages/recordFilling/detail.vue

@@ -3,12 +3,12 @@
     class="page"
     ref="paging"
     v-model="dataList"
-    :loading-more-enabled="false"
+    :loading-more-enabled="true"
     @query="queryList"
   >
     <!-- z-paging默认铺满全屏,此时页面所有view都应放在z-paging标签内,否则会被盖住 -->
     <!-- 需要固定在页面顶部的view请通过slot="top"插入,包括自定义的导航栏 -->
-    <view class="list">
+    <template #top>
       <!-- 工单基础信息 -->
       <view class="item top">
         <view class="item-content flex-row align-center">
@@ -36,7 +36,9 @@
           </view>
         </view>
       </view>
-      <!-- 填报列表 -->
+    </template>
+    <!-- 填报列表 -->
+    <view class="list">
       <view class="item" v-for="(item, index) in dataList" :key="index">
         <view class="item-module flex-row align-center justify-between">
           <view class="module-name">
@@ -127,11 +129,12 @@
             </view>
             <!-- type为enum时,使用下拉菜单 -->
             <view
-              class="item-value textarea flex-row align-center justify-end"
+              class="item-value select flex-row align-center justify-end"
               v-else-if="nosum.type == 'enum' && nosum.description !== null"
             >
               <uni-data-select
                 :localdata="nosum.enumList"
+                style="text-align: right"
                 :styles="{ disableColor: '#fff' }"
                 :clear="false"
                 :disabled="!isView"
@@ -162,6 +165,8 @@
         style="border-radius: 0"
         type="primary"
         @click="onSubmit()"
+        :disabled="dataList.length < totalNum"
+        v-if="isView && dataList.length === totalNum"
       >
         {{ $t("operation.save") }}
       </button>
@@ -179,6 +184,8 @@ import {
   recordFillingDetailInsertLog,
   getRecordFillingDetail,
   recordFillingUpOperationOrder,
+  recordFillingDetailGetPageAndAttrs,
+  recordFillingDetailInsertDataList,
 } from "@/api/recordFilling";
 import { getUserId, reloginByUserId } from "@/utils/auth.js";
 import { useDataDictStore } from "@/store/modules/dataDict";
@@ -235,6 +242,8 @@ onLoad(async (option) => {
 const paging = ref(null);
 // v-model绑定的这个变量不要在分页请求结束中自己赋值,直接使用即可
 const dataList = ref([]);
+// 列表总数
+const totalNum = ref(0);
 
 // 监听dataList变化,初始化时计算一次总和
 watch(
@@ -340,142 +349,127 @@ const queryList = (pageNo, pageSize) => {
     return;
   }
 
-  // 此处请求仅为演示,请替换为自己项目中的请求
-  getRecordFillingDetailGetPage({
-    // pageNo,
-    // pageSize,
-    ...params.value,
-    deviceCategoryId: 1,
+  // 请求填报设备及属性
+  recordFillingDetailGetPageAndAttrs({
+    pageNo,
+    pageSize,
+    orderId: params.value.orderId,
+    // deviceCategoryId: 1,
   })
     .then(async (res) => {
-      const resList = [].concat(res.data);
-
-      // 使用Promise.all等待所有异步请求完成
-      await Promise.all(
-        resList.map(async (item) => {
-          try {
-            const attrParams = {
-              deviceCode: item.deviceCode,
-              deviceName: item.deviceName,
-              deptId: item.deptId,
-              createTime: params.value.createTime,
-              deviceCategoryId: item.deviceCategoryId,
-              deviceId: item.deviceId,
-              userId: params.value.userId,
-              orderId: params.value.orderId,
-            };
-            // console.log(
-            //   "getRecordFillingDetailGetAttrs- attrParams",
-            //   attrParams
-            // );
-            const resAttrs = await getRecordFillingDetailGetAttrs({
-              pageNo: 1,
-              pageSize: 10,
-              ...attrParams,
-            });
-            // console.log("resAttrs", resAttrs);
-            if (resAttrs?.data) {
-              attrParams.createTime = attrParams.createTime
-                ? dayjs(attrParams.createTime).format("YYYY-MM-DD")
-                : "";
-              attrParams.id = attrParams.orderId;
-              delete attrParams.orderId;
-              delete attrParams.deviceName;
-              resAttrs.data.map((rtem) => {
-                // 将rtem中sumList和nonSumList两个数组中的
-                // fillContent字段判断是否为null, 如果为null,则赋值为0 不为null则保留两位小数
-                // 将attrParams合并到两个数组的每个对象中
-                // 然后将sumList和nonSumList分别赋值给item的sumList和nonSumList
-
-                if (rtem.sumList) {
-                  rtem.sumList.map((sumItem) => {
-                    if (
-                      sumItem.fillContent == null ||
-                      sumItem.fillContent == ""
-                    ) {
-                      // console.log("🚀 ~ rtem.sumList.map ~ sumItem:", sumItem);
-                      // sumItem.fillContent = 0;
-                    } else {
-                      // 如果是double类型,保留两位小数
-                      if (sumItem.type == "double") {
-                        sumItem.fillContent = toFixed(sumItem.fillContent);
-                      }
-                    }
-                    // 将sumItem的id赋值给modelId
-                    sumItem.modelId = sumItem.id;
-
-                    sumItem.pointName = sumItem.name;
-                    // 合并attrParams到sumItem中
-                    sumItem = Object.assign(sumItem, attrParams);
-                  });
+      console.log("🚀 ~ res:", res);
+      const { data } = res;
+      const resList = [].concat(data.list);
+      // 列表总数
+      totalNum.value = data.total;
+      // 遍历列表,处理attrsDetail
+      resList.map(async (item) => {
+        const attrParams = {
+          deviceCode: item.deviceCode,
+          deviceName: item.deviceName,
+          deptId: item.deptId,
+          createTime: params.value.createTime,
+          deviceCategoryId: item.deviceCategoryId,
+          deviceId: item.deviceId,
+          userId: params.value.userId,
+          orderId: params.value.orderId,
+        };
+        // console.log(
+        //   "getRecordFillingDetailGetAttrs- attrParams",
+        //   attrParams
+        // );
+        const resAttrs = item?.attrsDetail;
+        // console.log("resAttrs", resAttrs);
+        if (resAttrs) {
+          attrParams.createTime = attrParams.createTime
+            ? dayjs(attrParams.createTime).format("YYYY-MM-DD")
+            : "";
+          attrParams.id = attrParams.orderId;
+          delete attrParams.orderId;
+          delete attrParams.deviceName;
+          resAttrs.map((rtem) => {
+            // 将rtem中sumList和nonSumList两个数组中的
+            // fillContent字段判断是否为null, 如果为null,则赋值为0 不为null则保留两位小数
+            // 将attrParams合并到两个数组的每个对象中
+            // 然后将sumList和nonSumList分别赋值给item的sumList和nonSumList
+
+            if (rtem.sumList) {
+              rtem.sumList.map((sumItem) => {
+                if (sumItem.fillContent == null || sumItem.fillContent == "") {
+                  // console.log("🚀 ~ rtem.sumList.map ~ sumItem:", sumItem);
+                  // sumItem.fillContent = 0;
+                } else {
+                  // 如果是double类型,保留两位小数
+                  if (sumItem.type == "double") {
+                    sumItem.fillContent = toFixed(sumItem.fillContent);
+                  }
                 }
-                if (rtem.nonSumList) {
-                  //
-                  rtem.nonSumList.map((nonSumItem) => {
-                    if (
-                      nonSumItem.fillContent == null ||
-                      nonSumItem.fillContent == ""
-                    ) {
-                      // console.log(
-                      //   "🚀 ~ rtem.nonSumList.map ~ nonSumItem:",
-                      //   nonSumItem
-                      // );
-                      // nonSumItem.fillContent = 0;
-                    } else {
-                      // 如果是double类型,保留两位小数
-                      if (nonSumItem.type == "double") {
-                        nonSumItem.fillContent = toFixed(
-                          nonSumItem.fillContent
+                // 将sumItem的id赋值给modelId
+                sumItem.modelId = sumItem.id;
+
+                sumItem.pointName = sumItem.name;
+                // 合并attrParams到sumItem中
+                sumItem = Object.assign(sumItem, attrParams);
+              });
+            }
+            if (rtem.nonSumList) {
+              //
+              rtem.nonSumList.map((nonSumItem) => {
+                if (
+                  nonSumItem.fillContent == null ||
+                  nonSumItem.fillContent == ""
+                ) {
+                  // console.log(
+                  //   "🚀 ~ rtem.nonSumList.map ~ nonSumItem:",
+                  //   nonSumItem
+                  // );
+                  // nonSumItem.fillContent = 0;
+                } else {
+                  // 如果是double类型,保留两位小数
+                  if (nonSumItem.type == "double") {
+                    nonSumItem.fillContent = toFixed(nonSumItem.fillContent);
+                  }
+                }
+                nonSumItem.pointName = nonSumItem.name;
+                // 将nonSumItem的id赋值给modelId
+                nonSumItem.modelId = nonSumItem.id;
+                // 合并attrParams到nonSumItem中
+                nonSumItem = Object.assign(nonSumItem, attrParams);
+                // 如果是enum类型,且description不为null,则根据description获取对应字典项数组,赋值给enumList
+                if (nonSumItem.type == "enum" && nonSumItem.description) {
+                  console.log("🚀 ~ onSumItem.description:");
+                  nonSumItem.enumList =
+                    nonSumItem.name === "非生产原因"
+                      ? getIntDictOptions(nonSumItem.description).map(
+                          (dict) => {
+                            return {
+                              ...dict,
+                              text: dict.label,
+                            };
+                          }
+                        )
+                      : getStrDictOptions(nonSumItem.description).map(
+                          (dict) => {
+                            return {
+                              ...dict,
+                              text: dict.label,
+                            };
+                          }
                         );
-                      }
-                    }
-                    nonSumItem.pointName = nonSumItem.name;
-                    // 将nonSumItem的id赋值给modelId
-                    nonSumItem.modelId = nonSumItem.id;
-                    // 合并attrParams到nonSumItem中
-                    nonSumItem = Object.assign(nonSumItem, attrParams);
-                    // 如果是enum类型,且description不为null,则根据description获取对应字典项数组,赋值给enumList
-                    if (nonSumItem.type == "enum" && nonSumItem.description) {
-                      console.log("🚀 ~ onSumItem.description:");
-                      nonSumItem.enumList =
-                        nonSumItem.name === "非生产原因"
-                          ? getIntDictOptions(nonSumItem.description).map(
-                              (dict) => {
-                                return {
-                                  ...dict,
-                                  text: dict.label,
-                                };
-                              }
-                            )
-                          : getStrDictOptions(nonSumItem.description).map(
-                              (dict) => {
-                                return {
-                                  ...dict,
-                                  text: dict.label,
-                                };
-                              }
-                            );
-                      console.log(
-                        "🚀 ~  nonSumItem.enumList:",
-                        nonSumItem.enumList
-                      );
-                    }
-                  });
+                  console.log(
+                    "🚀 ~  nonSumItem.enumList:",
+                    nonSumItem.enumList
+                  );
                 }
-                item.sumList = rtem.sumList;
-
-                item.nonSumList = rtem.nonSumList;
               });
-              console.log("resAttrs-modelId", resAttrs);
             }
-          } catch (error) {
-            console.error("获取属性失败", error);
-            // 可以选择设置默认值或标记错误状态
-            item.sumList = [];
-            item.nonSumList = [];
-          }
-        })
-      );
+            item.sumList = rtem.sumList;
+
+            item.nonSumList = rtem.nonSumList;
+          });
+          console.log("resAttrs-modelId", resAttrs);
+        }
+      });
       console.log("resList--", resList);
       // 将请求结果通过complete传给z-paging处理,同时也代表请求结束,这一行必须调用
       paging.value.complete(resList);
@@ -530,7 +524,7 @@ const toFixed = (num) => {
 };
 
 const onSubmit = async () => {
-  console.log("onSubmit", dataList.value);
+  // console.log("onSubmit", dataList.value);
   // 1. 校验所有必填项
   // 遍历dataList.value中nonSumList每个item(非生产日报 isReport!=1)的fillContent字段,
   // 如果为null或者为空,则提示用户填写,
@@ -613,57 +607,56 @@ const onSubmit = async () => {
       }
     }
   }
-  console.log("提交用的副本数据:subDataList", subDataList);
-
-  try {
-    // 2. 收集所有保存请求(不在这里处理导航)
-    const submitPromises = subDataList.map(async (item) => {
-      const submitData = [].concat(item.sumList).concat(item.nonSumList);
-      // 仅返回接口结果,不执行导航
-      return await recordFillingDetailInsertLog(submitData);
-    });
-
-    // 3. 等待所有请求完成
-    const results = await Promise.all(submitPromises);
-
-    // 4. 所有请求完成后,统一判断结果
-    const allSuccess = results.every((res) => res?.code === 0);
-    if (allSuccess) {
-      // 5. 调用更新工单状态接口
-      const upRes = await recordFillingUpOperationOrder({
-        id: params.value.orderId,
-      });
-      console.log("🚀 ~ upRes:", upRes)
-      if (upRes?.code === 0) {
-        console.log("工单状态更新成功");
-        uni.showToast({
-          title: t("operation.success"),
-          duration: 1500,
-          icon: "none",
-        });
-        setTimeout(() => {
-          uni.navigateBack();
-        }, 1500);
-      } else {
-        console.error("工单状态更新失败", upRes);
-        uni.showToast({
-          title: t("operation.fail"),
-          icon: "none",
-        });
-      }
-    } else {
+  console.log("处理提交用的副本数据:subDataList", subDataList);
+  // 2. 处理提交数据:将nonSumList和sumList合并为新数组并赋值给deviceInfoList对象,将所有的deviceInfoList合并为submitList
+  const submitList = subDataList.map((item) => ({
+    deviceInfoList: [].concat(item.sumList).concat(item.nonSumList),
+  }));
+  console.log("提交用的数据:submitList", submitList);
+  // 3. 提交所有填写记录
+
+  await recordFillingDetailInsertDataList(submitList)
+    .then(async (res) => {
+      console.log("🚀 ~ 提交工单填报内容结果 ~ res:", res);
+      setTimeout(async () => {
+        if (res?.code === 0) {
+          // 3. 调用更新工单状态接口
+          const upRes = await recordFillingUpOperationOrder({
+            id: params.value.orderId,
+          });
+          console.log("🚀 ~ upRes:", upRes);
+          if (upRes?.code === 0) {
+            console.log("工单状态更新成功");
+            uni.showToast({
+              title: t("operation.success"),
+              duration: 1500,
+              icon: "none",
+            });
+            setTimeout(() => {
+              uni.navigateBack();
+            }, 1500);
+          } else {
+            console.error("工单状态更新失败", upRes);
+            uni.showToast({
+              title: t("operation.fail"),
+              icon: "none",
+            });
+          }
+        } else {
+          uni.showToast({
+            title: t("operation.fail"),
+            icon: "none",
+          });
+        }
+      }, 5000);
+    })
+    .catch((error) => {
+      console.error("保存失败", error);
       uni.showToast({
         title: t("operation.fail"),
-        icon: "none",
+        icon: "error",
       });
-    }
-  } catch (error) {
-    console.error("保存失败", error);
-    uni.showToast({
-      title: t("operation.fail"),
-      icon: "error",
     });
-  }
 };
 </script>
 
@@ -680,7 +673,7 @@ const onSubmit = async () => {
 .list {
   // margin-top: calc(10px);
   padding: 10px;
-  height: calc(100%);
+  // height: calc(100%);
 }
 
 .item {
@@ -772,6 +765,9 @@ const onSubmit = async () => {
     font-size: 16px !important;
   }
 }
+:deep(.uni-select__input-text) {
+  text-align: right;
+}
 :deep(.uni-select--disabled) {
   color: #d5d5d5 !important;
   background-color: transparent;

+ 1 - 1
request/index.js

@@ -47,7 +47,7 @@ function closeLoading() {
  */
 const http = new Request({
   baseURL: baseUrl + apiPath,
-  timeout: 8000,
+  timeout: 60000,
   method: 'GET',
   header: {
     Accept: 'text/json',