detail.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. <template>
  2. <z-paging
  3. class="page"
  4. ref="paging"
  5. v-model="dataList"
  6. :loading-more-enabled="true"
  7. @query="queryList"
  8. >
  9. <!-- z-paging默认铺满全屏,此时页面所有view都应放在z-paging标签内,否则会被盖住 -->
  10. <!-- 需要固定在页面顶部的view请通过slot="top"插入,包括自定义的导航栏 -->
  11. <template #top>
  12. <!-- 工单基础信息 -->
  13. <view class="item top">
  14. <view class="item-content flex-row align-center">
  15. <view class="item-title full-cell flex-row align-center">
  16. <span class="item-title-width"
  17. >{{ $t("operationRecordFilling.workOrderName") }}:</span
  18. >
  19. <span>{{ params.orderName }}</span>
  20. </view>
  21. </view>
  22. <view class="item-content flex-row align-center">
  23. <view class="item-title full-cell flex-row align-center">
  24. <span class="item-title-width"
  25. >{{ $t("operationRecordFilling.responsiblePerson") }}:</span
  26. >
  27. <span>{{ params.userName }}</span>
  28. </view>
  29. </view>
  30. <view class="item-content flex-row align-center">
  31. <view class="item-title full-cell flex-row align-center">
  32. <span class="item-title-width"
  33. >{{ $t("operation.createTime") }}:</span
  34. >
  35. <span>{{ params.createTime }}</span>
  36. </view>
  37. </view>
  38. </view>
  39. </template>
  40. <!-- 填报列表 -->
  41. <view class="list">
  42. <view class="item" v-for="(item, index) in dataList" :key="index">
  43. <view class="item-module flex-row align-center justify-between">
  44. <view class="module-name">
  45. {{ item.deviceCode }}({{ item.deviceName }})
  46. </view>
  47. <view class="module-border"> </view>
  48. </view>
  49. <view class="item-content flex-row align-center justify-between bold">
  50. <view class="item-title flex-row align-center">
  51. <span>{{ $t("operationRecordFilling.belongToTeam") }}:</span>
  52. <span>{{ item.orgName }}</span>
  53. </view>
  54. </view>
  55. <view
  56. class="item-content flex-row align-center justify-between bold"
  57. v-for="sum in item.sumList"
  58. >
  59. <view class="item-title flex-row align-center word-break-all">
  60. <span>{{ sum.name }}:</span>
  61. </view>
  62. <view class="item-value flex-row align-center justify-end total">
  63. <uni-easyinput
  64. style="text-align: right"
  65. :inputBorder="false"
  66. :clearable="true"
  67. :styles="{ disableColor: '#fff' }"
  68. :value="`${sum.totalRunTime} ${
  69. sum.modelAttr ? (sum.modelAttr.includes('Time') ? 'h' : '') : ''
  70. }`"
  71. :disabled="true"
  72. ></uni-easyinput>
  73. </view>
  74. </view>
  75. <view
  76. class="item-content flex-col align-center justify-between"
  77. :class="{ 'bottom-bold': item.nonSumList.length > 0 }"
  78. v-for="nosum in item.nonSumList"
  79. >
  80. <!-- isCollection为1,提示:以下数值取自PLC,如有不符请修改 -->
  81. <uni-notice-bar
  82. :text="$t('operationRecordFilling.plcNotice')"
  83. v-if="nosum.isCollection == 1"
  84. />
  85. <view class="flex-row align-center justify-between item-content">
  86. <view class="item-title flex-row align-center">
  87. <span>{{ nosum.name }}:</span>
  88. </view>
  89. <!-- 判断填写项的属性 -->
  90. <!-- type为double时,输入框为数字类型 -->
  91. <view
  92. class="item-value flex-row align-center justify-end"
  93. v-if="nosum.type == 'double'"
  94. >
  95. <uni-easyinput
  96. style="text-align: right"
  97. :styles="{ disableColor: '#fff' }"
  98. :inputBorder="false"
  99. :clearable="true"
  100. :placeholder="$t('operation.PleaseFillIn')"
  101. :disabled="!isView"
  102. v-model="nosum.fillContent"
  103. :type="'digit'"
  104. @blur="
  105. nosum.threshold > 0
  106. ? checkThreshold(nosum)
  107. : checkLessThreshold(nosum)
  108. "
  109. @input="handleRealTimeUpdate(nosum, item)"
  110. ></uni-easyinput>
  111. </view>
  112. <!-- type为textarea时,输入框为文本类型 -->
  113. <view
  114. class="item-value flex-row align-center justify-end"
  115. v-else-if="nosum.type == 'textarea'"
  116. >
  117. <uni-easyinput
  118. style="text-align: right"
  119. :styles="{ disableColor: '#fff' }"
  120. :inputBorder="false"
  121. :clearable="true"
  122. :placeholder="$t('operation.PleaseFillIn')"
  123. :disabled="!isView"
  124. v-model="nosum.fillContent"
  125. :type="'textarea'"
  126. :autoHeight="true"
  127. :maxlength="-1"
  128. ></uni-easyinput>
  129. </view>
  130. <!-- type为enum时,使用下拉菜单 -->
  131. <view
  132. class="item-value select flex-row align-center justify-end"
  133. v-else-if="nosum.type == 'enum' && nosum.description !== null"
  134. >
  135. <uni-data-select
  136. :localdata="nosum.enumList"
  137. style="text-align: right"
  138. :styles="{ disableColor: '#fff' }"
  139. :clear="false"
  140. :disabled="!isView"
  141. :placeholder="$t('operation.PleaseSelect')"
  142. v-model="nosum.fillContent"
  143. ></uni-data-select>
  144. </view>
  145. <!-- 其他类型时,输入框为文本类型 -->
  146. <view class="item-value flex-row align-center justify-end" v-else>
  147. <uni-easyinput
  148. style="text-align: right"
  149. :styles="{ disableColor: '#fff' }"
  150. :inputBorder="false"
  151. :clearable="true"
  152. :placeholder="$t('operation.PleaseFillIn')"
  153. :disabled="!isView"
  154. v-model="nosum.fillContent"
  155. :type="'text'"
  156. ></uni-easyinput>
  157. </view>
  158. </view>
  159. </view>
  160. </view>
  161. </view>
  162. <!-- 如果需要使用页脚,请使用slot="bottom"slot节点不支持通过v-if或v-show动态显示/隐藏,若需要动态控制,可将v-if添加在其子节点上 -->
  163. <template #bottom>
  164. <button
  165. style="border-radius: 0"
  166. type="primary"
  167. @click="onSubmit()"
  168. :disabled="dataList.length < totalNum"
  169. v-if="isView"
  170. >
  171. {{ $t("operation.save") }}
  172. </button>
  173. </template>
  174. </z-paging>
  175. </template>
  176. <script setup>
  177. import { ref, reactive, getCurrentInstance, watch, onMounted } from "vue";
  178. import { onReady, onLoad } from "@dcloudio/uni-app";
  179. import dayjs from "dayjs";
  180. import {
  181. getRecordFillingDetailGetPage,
  182. getRecordFillingDetailGetAttrs,
  183. recordFillingDetailInsertLog,
  184. getRecordFillingDetail,
  185. recordFillingUpOperationOrder,
  186. recordFillingDetailGetPageAndAttrs,
  187. recordFillingDetailInsertDataList,
  188. } from "@/api/recordFilling";
  189. import { getUserId, reloginByUserId } from "@/utils/auth.js";
  190. import { useDataDictStore } from "@/store/modules/dataDict";
  191. // 引用全局变量$t
  192. const { appContext } = getCurrentInstance();
  193. const t = appContext.config.globalProperties.$t;
  194. // 获取字典项
  195. const { getStrDictOptions, getIntDictOptions } = useDataDictStore();
  196. // -------------------------------------
  197. const isFromMsg = ref(false);
  198. const params = ref({});
  199. const isView = ref(false); // 是否编辑 -- view == 1为编辑状态
  200. onMounted(() => {
  201. console.log("onMounted");
  202. });
  203. onReady(() => {
  204. console.log("onReady");
  205. });
  206. onLoad(async (option) => {
  207. console.log("onLoad", option);
  208. await reloginByUserId(option.reloginUserId);
  209. isFromMsg.value = !!option.reloginUserId;
  210. // 初始化params
  211. params.value = JSON.parse(option.param);
  212. // 处理createTime
  213. params.value.createTime = params.value.createTime
  214. ? dayjs(Number.parseInt(params.value.createTime)).format("YYYY-MM-DD")
  215. : "";
  216. // 请求工单详情
  217. if (params.value?.orderId) {
  218. const detail = (await getRecordFillingDetail(params.value.orderId)).data;
  219. console.log("🚀getRecordFillingDetail ~ detail:", detail);
  220. params.value = {
  221. ...params.value,
  222. ...detail,
  223. // 处理createTime
  224. createTime: detail.createTime
  225. ? dayjs(Number.parseInt(detail.createTime)).format("YYYY-MM-DD")
  226. : "",
  227. orderId: detail.id,
  228. };
  229. }
  230. console.log("🚀 ~ params.value:", params.value);
  231. // 处理是否可编辑 {0: '待填写', 1: '已完成', 2: '填写中', 3: '忽略'}
  232. isView.value = params.value?.orderStatus % 2 == 0;
  233. console.log("🚀 ~ isView.value:", isView.value);
  234. });
  235. const paging = ref(null);
  236. // v-model绑定的这个变量不要在分页请求结束中自己赋值,直接使用即可
  237. const dataList = ref([]);
  238. // 列表总数
  239. const totalNum = ref(0);
  240. // 监听dataList变化,初始化时计算一次总和
  241. watch(
  242. dataList,
  243. (newVal) => {
  244. // calculateTotalRunTime();
  245. },
  246. { deep: true }
  247. );
  248. // 处理fillContent变化的方法
  249. const handleFillContentChange = (nosum, deviceItem) => {
  250. console.log("🚀 ~ nosum, deviceItem:", nosum, deviceItem);
  251. // 处理增压机
  252. if (
  253. deviceItem.deviceName.includes("增压机") &&
  254. nosum.name === "当日运转时间"
  255. ) {
  256. calculateTotalRunTime("增压机", "当日运转时间"); // 计算当日运转时间总和
  257. }
  258. // 处理提纯撬
  259. if (deviceItem.deviceName.includes("提纯撬") && nosum.name === "当日注气量") {
  260. calculateTotalRunTime("提纯撬", "当日注气量"); // 计算当日注气量总和
  261. }
  262. // 处理注水泵
  263. if (deviceItem.deviceName.includes("注水泵") && nosum.name === "当日注水量") {
  264. calculateTotalRunTime("注水泵", "当日注水量"); // 计算当日注水量总和
  265. }
  266. // 处理箱式变电站
  267. if (
  268. deviceItem.deviceName.includes("箱式变电站") &&
  269. nosum.name === "当日用电量"
  270. ) {
  271. calculateTotalRunTime("箱式变电站", "当日用电量"); // 计算当日用电量总和
  272. }
  273. };
  274. // 防抖函数
  275. function debounce(func, delay) {
  276. let timer;
  277. return function (...args) {
  278. clearTimeout(timer);
  279. timer = setTimeout(() => func.apply(this, args), delay);
  280. };
  281. }
  282. //防抖
  283. const debouncedCalculateTotalRunTime = debounce(calculateTotalRunTime, 100);
  284. const handleRealTimeUpdate = (nosum, deviceItem) => {
  285. console.log("🚀 实时更新 ~ nosum, deviceItem:", nosum, deviceItem);
  286. // 当日运转时间累加
  287. if (
  288. deviceItem.deviceName.includes("增压机") &&
  289. nosum.name === "当日运转时间H"
  290. ) {
  291. debouncedCalculateTotalRunTime("增压机", "当日运转时间H", "当日运转时间H");
  292. }
  293. // 当日注气量累加
  294. if (
  295. deviceItem.deviceName.includes("提纯撬") &&
  296. nosum.name === "当日注气量-方"
  297. ) {
  298. debouncedCalculateTotalRunTime("提纯撬", "当日注气量-方", "当日注气量-方");
  299. }
  300. // 当日注水量累加
  301. if (
  302. deviceItem.deviceName.includes("注水泵") &&
  303. nosum.name === "当日注水量-方"
  304. ) {
  305. debouncedCalculateTotalRunTime("注水泵", "当日注水量-方", "当日注水量-方");
  306. }
  307. // 当日用电量累加
  308. if (
  309. deviceItem.deviceName.includes("箱式变电站") &&
  310. nosum.name === "当日用电量kWh"
  311. ) {
  312. debouncedCalculateTotalRunTime(
  313. "箱式变电站",
  314. "当日用电量kWh",
  315. "当日用电量kWh"
  316. );
  317. }
  318. };
  319. /**
  320. * 计算所有deviceName中包含deviceNameToMatch的对象中对应的reportName的fillContent总和并更新到reportName的fillContent中
  321. * @param deviceNameToMatch {string} 设备名称包含的字符串
  322. * @param reportName {string} 填写项名称
  323. */
  324. function calculateTotalRunTime(
  325. deviceNameToMatch,
  326. sourceFieldName,
  327. targetFieldName
  328. ) {
  329. const reportItem = dataList.value.find(
  330. (item) => item.deviceName === "生产日报"
  331. );
  332. if (!reportItem) {
  333. console.warn("⚠️ 未找到生产日报");
  334. return;
  335. }
  336. const targetItem = reportItem.nonSumList.find(
  337. (item) => item.name === targetFieldName
  338. );
  339. if (!targetItem) {
  340. console.warn(`⚠️ 未找到目标字段:${targetFieldName}`);
  341. return;
  342. }
  343. let total = 0;
  344. dataList.value.forEach((item) => {
  345. if (item.deviceName.includes(deviceNameToMatch) && item.nonSumList) {
  346. item.nonSumList.forEach((nonSum) => {
  347. if (
  348. nonSum.type === "double" &&
  349. nonSum.name === sourceFieldName &&
  350. nonSum.fillContent !== null &&
  351. nonSum.fillContent !== ""
  352. ) {
  353. const value = parseFloat(nonSum.fillContent) || 0;
  354. total += value;
  355. }
  356. });
  357. }
  358. });
  359. console.log(`📊 累计值 (${sourceFieldName} -> ${targetFieldName}):`, total);
  360. targetItem.fillContent = toFixed(total);
  361. }
  362. // @query所绑定的方法不要自己调用!!需要刷新列表数据时,只需要调用paging.value.reload()即可
  363. const queryList = (pageNo, pageSize) => {
  364. const userId = uni.getStorageSync("userId");
  365. if (!userId) {
  366. paging.value.complete([]);
  367. return;
  368. }
  369. // 请求填报设备及属性
  370. recordFillingDetailGetPageAndAttrs({
  371. pageNo,
  372. pageSize,
  373. orderId: params.value.orderId,
  374. // deviceCategoryId: 1,
  375. })
  376. .then(async (res) => {
  377. console.log("🚀 ~ res:", res);
  378. const { data } = res;
  379. const resList = [].concat(data.list);
  380. // 列表总数
  381. totalNum.value = data.total;
  382. // 遍历列表,处理attrsDetail
  383. resList.map(async (item) => {
  384. const attrParams = {
  385. deviceCode: item.deviceCode,
  386. deviceName: item.deviceName,
  387. deptId: item.deptId,
  388. createTime: params.value.createTime,
  389. deviceCategoryId: item.deviceCategoryId,
  390. deviceId: item.deviceId,
  391. userId: params.value.userId,
  392. orderId: params.value.orderId,
  393. };
  394. // console.log(
  395. // "getRecordFillingDetailGetAttrs- attrParams",
  396. // attrParams
  397. // );
  398. const resAttrs = item?.attrsDetail;
  399. // console.log("resAttrs", resAttrs);
  400. if (resAttrs) {
  401. attrParams.createTime = attrParams.createTime
  402. ? dayjs(attrParams.createTime).format("YYYY-MM-DD")
  403. : "";
  404. attrParams.id = attrParams.orderId;
  405. delete attrParams.orderId;
  406. delete attrParams.deviceName;
  407. resAttrs.map((rtem) => {
  408. // 将rtem中sumList和nonSumList两个数组中的
  409. // fillContent字段判断是否为null, 如果为null,则赋值为0 不为null则保留两位小数
  410. // 将attrParams合并到两个数组的每个对象中
  411. // 然后将sumList和nonSumList分别赋值给item的sumList和nonSumList
  412. if (rtem.sumList) {
  413. rtem.sumList.map((sumItem) => {
  414. if (sumItem.fillContent == null || sumItem.fillContent == "") {
  415. // console.log("🚀 ~ rtem.sumList.map ~ sumItem:", sumItem);
  416. // sumItem.fillContent = 0;
  417. } else {
  418. // 如果是double类型,保留两位小数
  419. if (sumItem.type == "double") {
  420. sumItem.fillContent = toFixed(sumItem.fillContent);
  421. }
  422. }
  423. // 将sumItem的id赋值给modelId
  424. sumItem.modelId = sumItem.id;
  425. sumItem.pointName = sumItem.name;
  426. // 合并attrParams到sumItem中
  427. sumItem = Object.assign(sumItem, attrParams);
  428. });
  429. }
  430. if (rtem.nonSumList) {
  431. //
  432. rtem.nonSumList.map((nonSumItem) => {
  433. if (
  434. nonSumItem.fillContent == null ||
  435. nonSumItem.fillContent == ""
  436. ) {
  437. // console.log(
  438. // "🚀 ~ rtem.nonSumList.map ~ nonSumItem:",
  439. // nonSumItem
  440. // );
  441. // nonSumItem.fillContent = 0;
  442. } else {
  443. // 如果是double类型,保留两位小数
  444. if (nonSumItem.type == "double") {
  445. nonSumItem.fillContent = toFixed(nonSumItem.fillContent);
  446. }
  447. }
  448. nonSumItem.pointName = nonSumItem.name;
  449. // 将nonSumItem的id赋值给modelId
  450. nonSumItem.modelId = nonSumItem.id;
  451. // 合并attrParams到nonSumItem中
  452. nonSumItem = Object.assign(nonSumItem, attrParams);
  453. // 如果是enum类型,且description不为null,则根据description获取对应字典项数组,赋值给enumList
  454. if (nonSumItem.type == "enum" && nonSumItem.description) {
  455. console.log("🚀 ~ onSumItem.description:");
  456. const dictOptions =
  457. nonSumItem.name === "非生产原因"
  458. ? getIntDictOptions(nonSumItem.description)
  459. : getStrDictOptions(nonSumItem.description);
  460. nonSumItem.enumList = dictOptions.map((dict) => {
  461. return {
  462. ...dict,
  463. text: dict.label,
  464. };
  465. });
  466. // 确保 fillContent 的类型与 enumList 中的 value 类型匹配
  467. if (nonSumItem.name === "非生产原因") {
  468. // 如果是"非生产原因",将 fillContent 转换为数字类型以匹配 getIntDictOptions
  469. if (
  470. nonSumItem.fillContent !== null &&
  471. nonSumItem.fillContent !== ""
  472. ) {
  473. nonSumItem.fillContent = parseInt(nonSumItem.fillContent);
  474. }
  475. }
  476. console.log("🚀 ~ nonSumItem.enumList:", nonSumItem.enumList);
  477. }
  478. });
  479. }
  480. item.sumList = rtem.sumList;
  481. item.nonSumList = rtem.nonSumList;
  482. });
  483. console.log("resAttrs-modelId", resAttrs);
  484. }
  485. return item;
  486. });
  487. console.log("resList--", resList);
  488. // 将请求结果通过complete传给z-paging处理,同时也代表请求结束,这一行必须调用
  489. paging.value.completeByNoMore(
  490. resList,
  491. pageNo * pageSize >= totalNum.value
  492. );
  493. })
  494. .catch((res) => {
  495. // 如果请求失败写paging.value.complete(false);
  496. // 注意,每次都需要在catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
  497. // 在底层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
  498. paging.value.complete(false);
  499. });
  500. };
  501. // 判断是否小于阈值 (<0)
  502. const checkLessThreshold = (item) => {
  503. if (item.fillContent < 0) {
  504. uni.showToast({
  505. title:
  506. item.name +
  507. t("operationRecordFilling.fillContentCannotLessThanThreshold") +
  508. "0",
  509. icon: "none",
  510. });
  511. item.fillContent = ""; // 清空输入
  512. return false; // 返回false表示校验失败
  513. }
  514. };
  515. // 判断是否大于阈值
  516. const checkThreshold = (item) => {
  517. checkLessThreshold(item);
  518. // 如果threshold > 0,则判断fillContent是否大于threshold,如果大于则提示用户填写小于等于threshold的值
  519. if (item.fillContent > item.threshold) {
  520. uni.showToast({
  521. title:
  522. item.name +
  523. t("operationRecordFilling.fillContentCannotGreaterThanThreshold") +
  524. item.threshold,
  525. icon: "none",
  526. });
  527. item.fillContent = ""; // 清空输入
  528. return false; // 返回false表示校验失败
  529. }
  530. };
  531. // 保留两位小数
  532. const toFixed = (num) => {
  533. if (num) {
  534. num = Number(num);
  535. num = num.toFixed(2);
  536. } else {
  537. num = 0.0;
  538. }
  539. return num;
  540. };
  541. const onSubmit = async () => {
  542. // console.log("onSubmit", dataList.value);
  543. // 校验是否所有待填写项都已加载
  544. if (dataList.value.length < totalNum) {
  545. uni.showToast({
  546. title: t("operationRecordFilling.PleaseLoadAllItems"),
  547. icon: "none",
  548. });
  549. return; // 校验失败直接返回
  550. }
  551. // 1. 校验所有必填项
  552. // 遍历dataList.value中nonSumList每个item(非生产日报 isReport!=1)的fillContent字段,
  553. // 如果为null或者为空,则提示用户填写,
  554. // 如果threshold > 0,则判断fillContent是否大于threshold,如果大于则提示用户填写小于等于threshold的值
  555. // 如果所有项全部填写,则调用填写记录接口
  556. for (const item of dataList.value) {
  557. const nonSumList = Array.isArray(item.nonSumList) ? item.nonSumList : [];
  558. // 查找当日运转时间H项目
  559. const runtimeItem = nonSumList.find((i) => i.name === "当日运转时间H");
  560. const isRuntime24 =
  561. runtimeItem &&
  562. (runtimeItem.fillContent == 24 || runtimeItem.fillContent == "24");
  563. for (const nonSumItem of nonSumList) {
  564. // 增加判断条件:如果当日运转时间H等于24,则非生产原因和非生产时间H为非必填,否则为必填
  565. const isExemptField =
  566. isRuntime24 &&
  567. (nonSumItem.name === "非生产原因" || nonSumItem.name === "非生产时间H");
  568. if (
  569. (!item.isReport || item.isReport != 1) &&
  570. !isExemptField &&
  571. (nonSumItem.fillContent == null || nonSumItem.fillContent === "")
  572. ) {
  573. uni.showToast({
  574. title:
  575. t("operation.PleaseFillIn") +
  576. item.deviceCode +
  577. "(" +
  578. item.deviceName +
  579. ")" +
  580. t("operation.allItem"),
  581. icon: "none",
  582. });
  583. return; // 校验失败直接返回
  584. }
  585. if (nonSumItem.fillContent != "" && nonSumItem.fillContent != null) {
  586. console.log("🚀 ~ nonSumItem:", nonSumItem);
  587. console.log("🚀 ~ nonSumItem.fillContent:", nonSumItem.fillContent);
  588. // 先将值转换为字符串进行操作
  589. const fillContentStr = String(nonSumItem.fillContent);
  590. // 将字符串转换为数字
  591. const num = Number(fillContentStr);
  592. // 检查转换后的数字是否有效
  593. if (!isNaN(num)) {
  594. // 检查是否包含小数(使用字符串检查)
  595. if (fillContentStr.includes(".")) {
  596. // 保留两位小数(假设toFixed是你定义的保留两位小数的函数)
  597. nonSumItem.fillContent = toFixed(num);
  598. } else {
  599. // 转换为整数
  600. nonSumItem.fillContent = Math.floor(num);
  601. }
  602. }
  603. }
  604. // 如果threshold > 0,则判断fillContent是否大于threshold
  605. if (nonSumItem.threshold > 0) {
  606. if (nonSumItem.fillContent > nonSumItem.threshold) {
  607. uni.showToast({
  608. title:
  609. item.deviceCode +
  610. "(" +
  611. item.deviceName +
  612. ")" +
  613. nonSumItem.name +
  614. t(
  615. "operationRecordFilling.fillContentCannotGreaterThanThreshold"
  616. ) +
  617. nonSumItem.threshold,
  618. icon: "none",
  619. duration: 3000,
  620. });
  621. nonSumItem.fillContent = ""; // 清空输入
  622. return; // 校验失败直接返回
  623. }
  624. }
  625. }
  626. }
  627. // 定义新的dataList副本 用于提交数据,避免修改原数据
  628. const subDataList = JSON.parse(JSON.stringify(dataList.value));
  629. // 3. 处理副本:删除 enumList(仅修改副本,不影响原数据)
  630. for (const item of subDataList) {
  631. // 先判断 item.nonSumList 存在,避免空指针
  632. if (item.nonSumList && item.nonSumList.length) {
  633. for (const nonSumItem of item.nonSumList) {
  634. if (nonSumItem.enumList) {
  635. delete nonSumItem.enumList;
  636. }
  637. }
  638. }
  639. }
  640. console.log("处理提交用的副本数据:subDataList", subDataList);
  641. // 2. 处理提交数据:将nonSumList和sumList合并为新数组并赋值给deviceInfoList对象,将所有的deviceInfoList合并为submitList
  642. const submitList = subDataList.map((item) => ({
  643. deviceInfoList: [].concat(item.sumList).concat(item.nonSumList),
  644. }));
  645. console.log("提交用的数据:submitList", submitList);
  646. // 3. 提交所有填写记录
  647. await recordFillingDetailInsertDataList(submitList)
  648. .then(async (res) => {
  649. console.log("🚀 ~ 提交工单填报内容结果 ~ res:", res);
  650. if (res?.code === 0) {
  651. // 3. 调用更新工单状态接口
  652. const upRes = await recordFillingUpOperationOrder({
  653. id: params.value.orderId,
  654. });
  655. console.log("🚀 ~ upRes:", upRes);
  656. if (upRes?.code === 0) {
  657. console.log("工单状态更新成功");
  658. uni.showToast({
  659. title: t("operation.success"),
  660. duration: 1500,
  661. icon: "none",
  662. });
  663. setTimeout(() => {
  664. uni.navigateBack();
  665. }, 1500);
  666. } else {
  667. console.error("工单状态更新失败", upRes);
  668. uni.showToast({
  669. title: t("operation.fail"),
  670. icon: "none",
  671. });
  672. }
  673. } else {
  674. uni.showToast({
  675. title: t("operation.fail"),
  676. icon: "none",
  677. });
  678. }
  679. })
  680. .catch((error) => {
  681. console.error("保存失败", error);
  682. uni.showToast({
  683. title: t("operation.fail"),
  684. icon: "error",
  685. });
  686. });
  687. };
  688. </script>
  689. <style lang="scss" scoped>
  690. .page {
  691. padding: 0;
  692. box-sizing: border-box;
  693. }
  694. .top {
  695. padding: 10px;
  696. }
  697. .list {
  698. // margin-top: calc(10px);
  699. padding: 10px;
  700. // height: calc(100%);
  701. }
  702. .item {
  703. width: 100%;
  704. // height: 204px;
  705. background: #ffffff;
  706. border-radius: 6px;
  707. margin-bottom: 10px;
  708. box-sizing: border-box;
  709. padding: 20px 15px;
  710. }
  711. .item-module {
  712. width: 100%;
  713. height: 16px;
  714. position: relative;
  715. font-weight: 600;
  716. font-size: 14px;
  717. color: #333333;
  718. margin-bottom: 10px;
  719. .module-border {
  720. position: absolute;
  721. left: -15px;
  722. width: 0px;
  723. height: 12px;
  724. border: 1px solid #004098;
  725. }
  726. }
  727. .item-content {
  728. position: relative;
  729. width: 100%;
  730. // height: calc(38px);
  731. box-sizing: border-box;
  732. font-weight: 500;
  733. font-size: 14px;
  734. color: #333333;
  735. line-height: 20px;
  736. border-bottom: 1px dashed #cacccf;
  737. &:last-child {
  738. border-bottom: none;
  739. }
  740. &.bold {
  741. font-weight: 600;
  742. // :deep(.uni-easyinput__content-input){
  743. // padding-right: 0 !important;
  744. // }
  745. }
  746. &.bottom-bold {
  747. border-bottom: 1px dashed #cacccf;
  748. }
  749. }
  750. .item-title {
  751. position: relative;
  752. min-height: 38px;
  753. width: 55%;
  754. &.total {
  755. :deep(.is-disabled) {
  756. color: #333333 !important;
  757. }
  758. }
  759. &.full-cell {
  760. width: 100%;
  761. min-width: max-content;
  762. }
  763. }
  764. .item-value {
  765. width: 45%;
  766. position: relative;
  767. &.textarea {
  768. width: 65%;
  769. }
  770. }
  771. .word-break-all {
  772. min-width: unset;
  773. }
  774. :deep(.uni-select) {
  775. border: none;
  776. text-align: right;
  777. padding-right: 0;
  778. .uniui-bottom:before {
  779. content: "\e6b5" !important;
  780. font-size: 16px !important;
  781. }
  782. }
  783. :deep(.uni-select__input-text) {
  784. text-align: right;
  785. .align-left {
  786. text-align: right;
  787. }
  788. }
  789. :deep(.uni-select--disabled) {
  790. color: #d5d5d5 !important;
  791. background-color: transparent;
  792. .uni-select__input-text {
  793. color: #d5d5d5 !important;
  794. }
  795. }
  796. :deep(.uni-select__selector) {
  797. text-align: left;
  798. }
  799. :deep(.uni-select__selector-item) {
  800. border-bottom: 1px dashed #cacccf;
  801. text-align: left;
  802. }
  803. :deep(.uni-easyinput__content-textarea) {
  804. min-height: inherit;
  805. margin: 10px;
  806. }
  807. </style>