edit.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. <template>
  2. <view class="page maintenance">
  3. <view class="segmented-header">
  4. <uni-segmented-control
  5. :current="currentTab"
  6. :values="tabTitles"
  7. :style-type="styleType"
  8. :active-color="activeColor"
  9. @clickItem="onClickTabItem"
  10. />
  11. </view>
  12. <scroll-view scroll-y="true" class="segmented-content">
  13. <!-- 工单信息 -->
  14. <view class="work-order-info" v-show="currentTab === 0">
  15. <work-order-form
  16. ref="workOrderFormRef"
  17. :form-data="mainWorkOrder"
  18. :form-disabled="false"
  19. />
  20. </view>
  21. <!-- 保养项列表 -->
  22. <view class="work-order-bom-list" v-show="currentTab === 1">
  23. <view class="list-content all">
  24. <work-order-boms
  25. :boms-data="mainWorkOrderBom"
  26. :work-order-type="mainWorkOrder.type"
  27. :current-bom="currentBomData"
  28. @onSelectBom="onSelectBom"
  29. @updateStatistics="updateBomStatisticsAndCost"
  30. ></work-order-boms>
  31. </view>
  32. </view>
  33. <!--物料列表 -->
  34. <view class="material-list" v-show="currentTab === 2">
  35. <!-- 物料列表操作栏 -->
  36. <uni-row
  37. class="list-header flex-row align-center justify-start"
  38. :gutter="10"
  39. >
  40. <!-- 选择物料 -->
  41. <uni-col :span="8">
  42. <button
  43. class="list-header-item flex-row align-center justify-center"
  44. type="default"
  45. :disabled="
  46. !currentBomData ||
  47. currentBomData.rule == '1' ||
  48. currentBomData.status == '1'
  49. "
  50. @click="onMaterialChoose(currentBomData)"
  51. >
  52. <image
  53. src="~@/static/workOrder/xuanzhewuliao.png"
  54. mode="aspectFill"
  55. ></image>
  56. <text>{{ t("workOrder.selectMaterial") }}</text>
  57. </button>
  58. </uni-col>
  59. <!-- 所有物料/当前保养项物料 -->
  60. <uni-col :span="8">
  61. <button
  62. class="list-header-item flex-row align-center justify-center"
  63. type="default"
  64. @click="changeMaterialList"
  65. >
  66. <image
  67. src="~@/static/workOrder/suoyoueuliao.png"
  68. mode="aspectFill"
  69. ></image>
  70. <text v-if="allMaterialShow">
  71. {{ t("workOrder.currentMaterial") }}
  72. </text>
  73. <text v-else>{{ t("workOrder.allMaterial") }}</text>
  74. </button>
  75. </uni-col>
  76. <!-- 新增物料 -->
  77. <uni-col :span="8">
  78. <button
  79. class="list-header-item flex-row align-center justify-center"
  80. type="default"
  81. :disabled="
  82. !currentBomData ||
  83. currentBomData.rule == '1' ||
  84. currentBomData.status == '1'
  85. "
  86. @click="addMaterial"
  87. >
  88. <image
  89. src="~@/static/workOrder/xinzengwuliao.png"
  90. mode="aspectFill"
  91. ></image>
  92. <text>{{ t("workOrder.addMaterial") }}</text>
  93. </button>
  94. </uni-col>
  95. </uni-row>
  96. <view class="list-content">
  97. <work-order-materials
  98. :materials-data="materialList"
  99. :material-disabled="false"
  100. @deleteMaterial="onDeleteMaterial"
  101. @materialChanged="updateBomStatisticsAndCost"
  102. ></work-order-materials>
  103. </view>
  104. </view>
  105. </scroll-view>
  106. <!-- 底部总览 及 操作按钮 -->
  107. <view class="segmented-footer">
  108. <view
  109. class="footer-total flex-row align-center justify-between"
  110. v-if="currentTab < 2"
  111. >
  112. <view class="total-item flex-row align-center justify-around">
  113. <!-- 共xx项 -->
  114. <view class="total-item-text">
  115. <text>{{ t("workOrder.total") }}</text>
  116. <text class="number">{{ bomNumbers.total }}</text>
  117. <text>{{ t("workOrder.item") }}</text>
  118. </view>
  119. <!-- 已保养xx项 -->
  120. <view class="total-item-text">
  121. <text>{{ t("maintenanceWorkOrder.maintained") }}</text>
  122. <text class="number">{{ bomNumbers.maintained }}</text>
  123. <text>{{ t("workOrder.item") }}</text>
  124. </view>
  125. <!-- 未保养xx项 -->
  126. <view class="total-item-text color-red">
  127. <text>{{ t("maintenanceWorkOrder.notMaintained") }}</text>
  128. <text class="number">{{ bomNumbers.notMaintained }}</text>
  129. <text>{{ t("workOrder.item") }}</text>
  130. </view>
  131. </view>
  132. <view class="total-btn">
  133. <button class="mini-btn" type="primary" @click="submitWorkOrder">
  134. {{ t("operation.submit") }}
  135. </button>
  136. </view>
  137. </view>
  138. <view class="footer-btn" v-else>
  139. <button class="mini-btn" type="primary" @click="saveMartialList">
  140. {{ t("operation.save") }}{{ t("workOrder.currentMaterialList") }}
  141. </button>
  142. </view>
  143. </view>
  144. </view>
  145. <!-- 选择物料弹窗 -->
  146. <materials-choose
  147. ref="materialsChooseRef"
  148. :deptId="mainWorkOrder?.deptId"
  149. :deviceId="currentBomData?.deviceId"
  150. :bomNodeId="currentBomData?.bomNodeId"
  151. :noAdd="true"
  152. @material-submit="onMaterialChooseSubmit"
  153. />
  154. <!-- 全局提示信息弹窗 -->
  155. <global-message-popup
  156. class="edit-message-popup"
  157. ref="messageGlobalRef"
  158. :msgType="msgType"
  159. :messageText="messageText"
  160. />
  161. </template>
  162. <script setup>
  163. import { onLoad, onReady, onBackPress } from "@dcloudio/uni-app";
  164. import {
  165. reactive,
  166. ref,
  167. onMounted,
  168. onBeforeUnmount,
  169. nextTick,
  170. getCurrentInstance,
  171. watch,
  172. } from "vue";
  173. import dayjs from "dayjs";
  174. // --------------------------引入用户信息-------------------------------
  175. import { getDeptId, getUserId, reloginByUserId } from "@/utils/auth";
  176. // --------------------------引用api接口-----start------------------------
  177. import {
  178. fillWorkOrder,
  179. getMaintenanceDetail,
  180. getWorkOrderBOMs,
  181. getBomMaterialsByWorkOrderId,
  182. } from "@/api/maintenance";
  183. // --------------------------引用api接口-----end--------------------------
  184. // --------------------------引用组件-----start---------------------------
  185. import workOrderForm from "./components/work-order-form.vue";
  186. import workOrderBoms from "./components/work-order-boms.vue";
  187. import workOrderMaterials from "./components/work-order-materials.vue";
  188. import materialsChoose from "@/components/materials/choose.vue";
  189. // --------------------------引用组件-----end-----------------------------
  190. // --------------------------引用全局变量$t-------------------------------
  191. const { appContext } = getCurrentInstance();
  192. const t = appContext.config.globalProperties.$t;
  193. // ----------------------------选项卡----------------------------------
  194. // 选项卡标题
  195. const tabTitles = ref([
  196. t("workOrder.workOrderInfo"),
  197. t("workOrder.maintenanceWorkOrderList"),
  198. t("workOrder.materialList"),
  199. ]);
  200. const currentTab = ref(0);
  201. const styleType = ref("text");
  202. const activeColor = ref("#004098");
  203. const onClickTabItem = (e) => {
  204. // 如果要切换的选项卡是“物料列表”,则先判断当前选中的保养项是否关闭消耗物料(rule=1),如果是,则不允许切换到“物料列表”,并提示
  205. if (!onBeforeTabChange(e.currentIndex, currentBomData.value)) {
  206. return;
  207. }
  208. currentTab.value = e.currentIndex;
  209. };
  210. // 关闭物料消耗(rule=1)时,不允许切换到物料列表tab
  211. const onBeforeTabChange = (index, bom) => {
  212. if (index === 2 && bom?.rule == 1) {
  213. uni.showToast({
  214. title: t("maintenanceWorkOrder.notConsumeMaterial"),
  215. icon: "none",
  216. });
  217. return false;
  218. }
  219. return true;
  220. };
  221. // ----------------------------提示信息弹窗----------------------------------
  222. const messageGlobalRef = ref(null);
  223. // 提示信息弹窗类型
  224. const msgType = ref("success");
  225. // 提示信息弹窗内容
  226. const messageText = ref("");
  227. // 提示信息弹窗方法
  228. const showMessage = (type, message) => {
  229. msgType.value = type;
  230. messageText.value = message;
  231. messageGlobalRef.value.openMessage();
  232. };
  233. // -----------------------------表单----------------------------------
  234. const mainWorkOrder = ref({
  235. name: "", // 工单名称
  236. outsourcingFlag: "0", // 保养类型(是否委外 0否 1是)
  237. applicant: "", // 填报人
  238. actualStartTime: "", // 实际开始时间
  239. actualEndTime: "", // 实际结束时间
  240. remark: "", // 备注
  241. cost: "", // 保养费用
  242. otherCost: "", // 其他费用
  243. type: 2, //工单类型(1计划生成 2临时新建),
  244. deptId: null, // 部门id"
  245. responsiblePerson: getUserId(), // 负责人
  246. });
  247. // 获取子组件实例
  248. const workOrderFormRef = ref(null);
  249. // 提交方法中添加校验逻辑
  250. const validateForm = async () => {
  251. // 调用子组件的校验方法
  252. const isValid = await workOrderFormRef.value.validate();
  253. if (isValid) {
  254. // 校验通过,执行提交逻辑
  255. console.log("子组件表单校验通过,校验通过的表单数据:", mainWorkOrder.value);
  256. // 判断mainWorkOrder.value中的实际保养结束时间是否大于实际保养开始时间
  257. if (
  258. mainWorkOrder.value.actualEndTime &&
  259. mainWorkOrder.value.actualStartTime &&
  260. mainWorkOrder.value.actualEndTime < mainWorkOrder.value.actualStartTime
  261. ) {
  262. uni.showToast({
  263. title: t("maintenanceWorkOrder.timeNotBeEarlier"),
  264. icon: "none",
  265. });
  266. mainWorkOrder.value.actualEndTime = "";
  267. return false;
  268. }
  269. return true;
  270. } else {
  271. // 校验失败,不执行提交
  272. console.log("表单校验失败");
  273. return false;
  274. }
  275. };
  276. // 计算保养费用
  277. const calculateMaintenanceCost = () => {
  278. let totalCost = 0;
  279. mainWorkOrderBom.value.forEach((bom) => {
  280. // 条件1:关闭是否消耗物料(rule=1)
  281. const isNotConsumeMaterial = bom.rule == 1;
  282. // 检查是否设置了延保相关信息
  283. const hasDelaySetting = isBomDelaySet(bom);
  284. // 条件2:开启是否消耗物料(rule=0)且未设置延保信息
  285. if (!isNotConsumeMaterial && !hasDelaySetting) {
  286. // 遍历物料计算费用
  287. bom.deviceBomMaterials?.forEach((material) => {
  288. const quantity = Number(material.quantity) || 0;
  289. const unitPrice = Number(material.unitPrice) || 0;
  290. // 只计算消耗数量大于0的物料
  291. if (quantity > 0) {
  292. totalCost += quantity * unitPrice;
  293. }
  294. });
  295. }
  296. });
  297. // 更新工单保养费用(保留两位小数)
  298. mainWorkOrder.value.cost = totalCost.toFixed(2);
  299. };
  300. // ----------------------------------保养项-------------------------------
  301. // 保养项列表
  302. const mainWorkOrderBom = ref([]);
  303. // 保养项列表原数据(提交用:不修改deviceBomMaterials)
  304. const mainWorkOrderBomOriginal = ref([]);
  305. // 保养项统计
  306. const bomNumbers = reactive({
  307. total: 0, // 保养项总数
  308. maintained: 0, // 已保养项数
  309. notMaintained: 0, // 未保养项数
  310. });
  311. // 当前所选保养项
  312. const currentBomData = ref(null);
  313. // 切换当前保养项
  314. const onSelectBom = (bom) => {
  315. console.log("onSelectBom", bom);
  316. // 检查选择的保养项是否关闭消耗物料(rule=1),若关闭则提示且不跳转
  317. if (!onBeforeTabChange(2, bom)) {
  318. return;
  319. }
  320. // 更新当前所选保养项数据
  321. currentBomData.value = bom;
  322. // 更新展示的物料列表
  323. // 更新传递给子组件的物料列表
  324. materialList.value = bom.deviceBomMaterials || [];
  325. // 切换到物料列表tab
  326. currentTab.value = 2;
  327. };
  328. // 更新保养项统计数量(已保养和未保养)
  329. const updateBomStatistics = () => {
  330. let total = mainWorkOrderBom.value.length;
  331. let maintained = 0;
  332. mainWorkOrderBom.value.forEach((bom) => {
  333. // 计算物料总数
  334. calculateMaterialCount(bom);
  335. // 判断是否为已保养项
  336. const isMaintained = checkIfMaintained(bom);
  337. if (isMaintained) {
  338. // 已保养项数增加
  339. maintained++;
  340. // 添加已保养标识
  341. bom.maintainedFlag = true;
  342. } else {
  343. // 添加未保养标识
  344. bom.maintainedFlag = false;
  345. }
  346. });
  347. bomNumbers.total = total;
  348. bomNumbers.maintained = maintained;
  349. bomNumbers.notMaintained = total - maintained;
  350. };
  351. /**
  352. * 检查保养项是否满足已保养条件
  353. * @param {Object} bom - 保养项数据
  354. * @returns {boolean} - 是否满足已保养条件
  355. * @description
  356. * 1.保养状态(status 0保养中 1保养完成)为保养完成时:
  357. * 1). 关闭是否消耗物料(rule=1)
  358. * 2). 开启是否消耗物料(rule=0):存在物料列表,且所有物料的消耗数量均大于0
  359. * 2. 延保相关信息
  360. * 2.1 检查延迟保养规则是否启用
  361. * 2.2 若没有启用的规则,或启用规则但对应延迟值均不大于0
  362. *
  363. */
  364. const checkIfMaintained = (bom) => {
  365. // 检查保养状态是否为保养完成
  366. const bomStatusCompleted = isBomStatusCompleted(bom);
  367. if (bomStatusCompleted) {
  368. // 检查是否关闭消耗物料
  369. const isNotConsumeMaterial = isBomNotConsumeMaterial(bom);
  370. // 检查物料消耗数量是否均大于0
  371. const allMaterialsValid = checkAllMaterialsValid(bom);
  372. }
  373. /**
  374. * 检查是否设置了延保相关信息
  375. * 1. 检查延迟保养规则是否启用:mileageRule,naturalDateRule,runningTimeRule(0启用 1停用)
  376. * 2. 若启用规则但对应延迟值均不大于0
  377. */
  378. const hasDelaySetting = isBomDelaySet(bom);
  379. // 满足任一条件即视为已保养
  380. return bomStatusCompleted || hasDelaySetting;
  381. };
  382. // 保养项 保养状态 是否完成
  383. const isBomStatusCompleted = (bom) => {
  384. const isCompleted = bom.status == 1;
  385. // -----------添加物料保养状态标识------------
  386. bom.isStatusClosed = isCompleted;
  387. // 如果存在物料列表,将isStatusClosed存入每个物料项中
  388. if (bom.deviceBomMaterials.length > 0) {
  389. bom.deviceBomMaterials.forEach((material) => {
  390. material.isStatusClosed = isCompleted;
  391. });
  392. }
  393. return isCompleted;
  394. };
  395. // 保养项是否消耗物料
  396. const isBomNotConsumeMaterial = (bom) => {
  397. const isNotConsumeMaterial = bom.rule == "1";
  398. bom.isNotConsumeMaterial = isNotConsumeMaterial;
  399. // 如果存在物料列表,将isNotConsumeMaterial存入每个物料项中
  400. if (bom.deviceBomMaterials.length > 0) {
  401. bom.deviceBomMaterials.forEach((material) => {
  402. material.isNotConsumeMaterial = isNotConsumeMaterial;
  403. });
  404. }
  405. return isNotConsumeMaterial;
  406. };
  407. // 保养项是否设置了延保信息
  408. const isBomDelaySet = (bom) => {
  409. /**
  410. * 检查是否设置了延保相关信息
  411. * 1. 检查延迟保养规则是否启用:mileageRule,naturalDateRule,runningTimeRule(0启用 1停用)
  412. * 2. 若启用规则但对应延迟值均不大于0
  413. */
  414. const isAnyRuleActive =
  415. bom.mileageRule === 0 ||
  416. bom.naturalDateRule === 0 ||
  417. bom.runningTimeRule === 0;
  418. const hasDelaySetting =
  419. !isAnyRuleActive ||
  420. (isAnyRuleActive &&
  421. (bom.delayKilometers > 0 ||
  422. bom.delayNaturalDate > 0 ||
  423. bom.delayDuration > 0));
  424. // console.log("🚀 是否设置了延保相关信息:", hasDelaySetting);
  425. // 设置保养项是否设置了延保信息
  426. bom.isDelay = hasDelaySetting;
  427. // 如果存在物料列表,将isDelay存入每个物料项中
  428. if (bom.deviceBomMaterials.length > 0) {
  429. bom.deviceBomMaterials.forEach((material) => {
  430. // 添加物料是否延迟标志
  431. material.isDelay = hasDelaySetting;
  432. });
  433. }
  434. return hasDelaySetting;
  435. };
  436. // 检查所有物料消耗数量是否均大于0
  437. const checkAllMaterialsValid = (bom) => {
  438. if (!bom.deviceBomMaterials || bom.deviceBomMaterials.length === 0) {
  439. return false;
  440. }
  441. return bom.deviceBomMaterials.every((material) => {
  442. const quantity = Number(material.quantity);
  443. return !isNaN(quantity) && quantity > 0;
  444. });
  445. };
  446. // 计算保养项中所有物料的消耗数量累计
  447. const calculateMaterialCount = (bom) => {
  448. if (!bom.deviceBomMaterials || bom.deviceBomMaterials.length === 0) {
  449. bom.materialCount = 0;
  450. return;
  451. }
  452. // 物料数量为保养项下所有物料 消耗数量累计
  453. const total = bom.deviceBomMaterials.reduce((sum, material) => {
  454. const quantity = Number(material.quantity) || 0;
  455. return sum + quantity;
  456. }, 0);
  457. bom.materialCount = total;
  458. };
  459. // 更新保养项数量统计及保养费用
  460. const updateBomStatisticsAndCost = () => {
  461. // 更新保养项统计数量(已保养和未保养)
  462. updateBomStatistics();
  463. // 更新保养费用
  464. calculateMaintenanceCost();
  465. };
  466. // ----------------------------------物料列表---------------------------------
  467. // 所有物料列表
  468. const materialListAll = ref([]);
  469. // 获取的保养项物料列表
  470. const materialByWorkOrderIdList = ref([]);
  471. // 获取的保养项物料列表根据bomNodeId与id组合的对象
  472. const materialByWorkOrderIdKeyAndList = ref({});
  473. // 传递给子组件的物料列表
  474. const materialList = ref([]);
  475. // 是否展示所有物料
  476. const allMaterialShow = ref(false);
  477. // 新添加的物料项
  478. const newMaterialItem = ref({
  479. id: null, //物料id
  480. bomNodeId: null, // 保养项id
  481. bomName: null, // 保养项名称
  482. materialCode: null, // 物料编码
  483. materialName: null, // 物料名称
  484. unit: null, // 物料单位
  485. unitPrice: 0, // 物料单价
  486. quantity: 0, // 物料消耗数量
  487. materialSource: t("workOrder.manualAdd"), // 物料来源
  488. isNew: true, // 是否是新添加的物料
  489. });
  490. // 查询保养工单已经选择的所有物料
  491. const getBomsMaterialList = async (id) => {
  492. const getBomMaterialsByWorkOrderIdAsync = getBomMaterialsByWorkOrderId({
  493. workOrderId: id,
  494. });
  495. const bomMaterials = (await getBomMaterialsByWorkOrderIdAsync).data;
  496. if (bomMaterials && bomMaterials.length > 0) {
  497. materialByWorkOrderIdList.value = bomMaterials;
  498. // 根据bomNodeId与id组合保养项物料列表
  499. materialByWorkOrderIdList.value.forEach((material) => {
  500. const key = material.bomNodeId;
  501. if (materialByWorkOrderIdKeyAndList.value[key]) {
  502. materialByWorkOrderIdKeyAndList.value[key].push(material);
  503. } else {
  504. materialByWorkOrderIdKeyAndList.value[key] = [material];
  505. }
  506. });
  507. }
  508. };
  509. // 新增物料
  510. const addMaterial = () => {
  511. /**
  512. * 新增物料
  513. * 1. 不直接在materialList.value中添加新物料是因为:
  514. * 在展示所有物料时添加的新物料时再次切换到展示当前物料,新增的物料不会更新到当前保养项中
  515. * 2. 将新添加的物料项放入当前保养项的物料列表第一个
  516. */
  517. currentBomData.value.deviceBomMaterials.unshift({
  518. ...newMaterialItem.value,
  519. bomNodeId: currentBomData.value.bomNodeId,
  520. bomName: currentBomData.value.name,
  521. });
  522. // 更新展示的物料列表
  523. changeMaterialListByShow();
  524. // 更新保养项数量统计及保养费用
  525. updateBomStatisticsAndCost();
  526. };
  527. // 删除物料
  528. const onDeleteMaterial = (data) => {
  529. materialList.value.splice(data.index, 1);
  530. // 更新当前保养项的物料数量
  531. calculateMaterialCount(currentBomData.value);
  532. // 更新保养项数量统计及保养费用
  533. updateBomStatisticsAndCost();
  534. };
  535. // 选择物料
  536. const materialsChooseRef = ref(null);
  537. const onMaterialChoose = (item) => {
  538. console.log("onMaterialChoose", item);
  539. materialsChooseRef.value.open(item);
  540. };
  541. // 选择物料弹窗提交
  542. const onMaterialChooseSubmit = (item) => {
  543. console.log("onMaterialChooseSubmit", item);
  544. /**
  545. * 1. 检查当前物料是否已存在
  546. * 保养项id(bomNodeId)、
  547. * 物料编码(materialCode)或物料名称(materialName)、
  548. * 成本中心id(costCenterId)、
  549. * 工厂id(factoryId)、
  550. * 单价(unitPrice)、
  551. * 库位id(storageLocationId)
  552. * 全部相同时视为同一物料
  553. * 2. 若已存在,则累加数量
  554. * 3. 若不存在,则新增物料
  555. */
  556. // 是否存在相同物料,若存在则累加数量,若不存在则新增
  557. const existMaterial = currentBomData.value.deviceBomMaterials.find(
  558. (material) => {
  559. const materialChooseKey = `${
  560. material.materialCode ? material.materialCode : material.materialName
  561. }_${material.costCenterId}_${material.factoryId}_${material.unitPrice}_${
  562. material.storageLocationId
  563. }`;
  564. return materialChooseKey === item.chooseKey;
  565. }
  566. );
  567. if (existMaterial) {
  568. existMaterial.quantity += item.quantity;
  569. } else {
  570. currentBomData.value.deviceBomMaterials.unshift({
  571. ...item,
  572. bomNodeId: currentBomData.value.bomNodeId, // 保养项id
  573. bomName: currentBomData.value.name, // 保养项名称
  574. isNew: false, // 是否是新添加的物料
  575. });
  576. }
  577. // 更新展示的物料列表
  578. changeMaterialListByShow();
  579. // 更新保养项数量统计及保养费用
  580. updateBomStatisticsAndCost();
  581. };
  582. // 切换展示的物料列表
  583. const changeMaterialList = () => {
  584. // 切换展示所有物料状态
  585. allMaterialShow.value = !allMaterialShow.value;
  586. // 根据展示状态切换物料列表
  587. changeMaterialListByShow();
  588. };
  589. // 返回所有物料列表
  590. const getAllMaterialList = () => {
  591. const list = mainWorkOrderBom.value
  592. .map((bom) => {
  593. if (bom.deviceBomMaterials) {
  594. return bom.deviceBomMaterials;
  595. }
  596. })
  597. .flat();
  598. return list;
  599. };
  600. // 根据物料展示状态(allMaterialShow),切换展示的物料列表
  601. const changeMaterialListByShow = () => {
  602. // allMaterialShow 为 true 时展示所有物料列表
  603. if (allMaterialShow.value) {
  604. materialList.value = getAllMaterialList();
  605. } else {
  606. // allMaterialShow 为 false 时展示当前所选保养项的物料列表
  607. materialList.value = currentBomData.value.deviceBomMaterials;
  608. }
  609. };
  610. // 保存物料列表
  611. const saveMartialList = async () => {
  612. /**
  613. * 校验当前展示的物料列表
  614. * 1.物料列表为空时,提示请添加物料
  615. * 2.新增的物料(isNew=true)物料名称不能为空,单价不能为空,消耗数量>0
  616. * 3.已有的物料(isNew=false)单价不能为空,消耗数量>0
  617. * 记录所有不符合校验的物料保养项名称,拼接提示
  618. */
  619. console.log("保存物料列表:开始");
  620. // 校验物料列表是否为空
  621. if (materialList.value.length === 0) {
  622. uni.showToast({
  623. title: t("maintenanceWorkOrder.materialEmpty"),
  624. icon: "none",
  625. });
  626. return;
  627. }
  628. const invalidBomNames = [];
  629. materialList.value.map((material, index) => {
  630. // 校验新增的物料
  631. if (material.isNew) {
  632. if (
  633. !material.materialName ||
  634. !material.unitPrice ||
  635. !material.quantity ||
  636. material.quantity <= 0
  637. ) {
  638. invalidBomNames.push(
  639. material.bomName +
  640. ":" +
  641. (material.materialName
  642. ? material.materialName
  643. : t("workOrder.serialNumber") + "(" + (index + 1) + ")")
  644. );
  645. }
  646. } else {
  647. // 校验已有的物料
  648. if (!material.unitPrice || !material.quantity || material.quantity <= 0) {
  649. invalidBomNames.push(material.bomName + ":" + material.materialName);
  650. }
  651. }
  652. });
  653. // 去重
  654. const uniqueInvalidBomNames = [...new Set(invalidBomNames)];
  655. /**
  656. * 拆分提示
  657. * 若不符合校验的物料大于3,提示前3项,其余项用...表示
  658. * 若不符合保养项的数量小于3,则全部提示
  659. */
  660. if (uniqueInvalidBomNames.length > 0) {
  661. const msg =
  662. uniqueInvalidBomNames.slice(0, 3).join(";\n") +
  663. (uniqueInvalidBomNames.length > 3 ? "..." : "\n") +
  664. t("workOrder.materialInvalid");
  665. showMessage("error", msg);
  666. return;
  667. }
  668. // 校验通过,将tab切换到保养项列表并提示保存成功
  669. const msg = t("workOrder.currentMaterialList") + t("operation.saveSuccess");
  670. showMessage("success", msg);
  671. currentTab.value = 1;
  672. console.log("保存物料列表:结束");
  673. };
  674. // ----------------------------------工单提交---------------------------------
  675. const submitWorkOrder = async () => {
  676. // 校验表单信息
  677. const formIsValid = await validateForm();
  678. if (!formIsValid) return;
  679. // 校验是否存在未保养项
  680. if (bomNumbers.notMaintained > 0) {
  681. uni.showToast({
  682. title: t("maintenanceWorkOrder.unFinishedMaintenanceItems"),
  683. icon: "none",
  684. });
  685. return;
  686. }
  687. // 处理提交的参数
  688. // 提取数据处理逻辑为独立函数 - 使用对象解构赋值代替 delete 操作,避免修改原始数据
  689. const processBomData = (bomItems) => {
  690. console.log("🚀 ~ processBomData ~ bomItems:", bomItems);
  691. console.log(
  692. "🚀 ~ processBomData ~ mainWorkOrderBomOriginal.value:",
  693. mainWorkOrderBomOriginal.value
  694. );
  695. return bomItems.map((item) => {
  696. const {
  697. materialCount,
  698. maintainedFlag,
  699. deviceBomMaterials,
  700. isDelay,
  701. isNotConsumeMaterial,
  702. ...rest
  703. } = item;
  704. // 查找原始数据中对应的deviceBomMaterials
  705. const originalDeviceBomMaterials = mainWorkOrderBomOriginal.value.find(
  706. (bom) => bom.bomNodeId === item.bomNodeId
  707. )?.deviceBomMaterials;
  708. // 用原始数据的deviceBomMaterials重新赋值给 rest.deviceBomMaterials
  709. rest.deviceBomMaterials = originalDeviceBomMaterials;
  710. return rest;
  711. });
  712. };
  713. const processMaterialData = (materialItems) => {
  714. return materialItems.map((item) => {
  715. const {
  716. bomName,
  717. isNew,
  718. bomDisabled,
  719. isDelay,
  720. isNotConsumeMaterial,
  721. isStatusClosed,
  722. ...rest
  723. } = item;
  724. // 将 materialName/name和materialCode/code保持一致
  725. rest.materialName = rest.materialName ? rest.materialName : rest.name;
  726. rest.name = rest.materialName;
  727. rest.materialCode = rest.materialCode ? rest.materialCode : rest.code;
  728. rest.code = rest.materialCode;
  729. return rest;
  730. });
  731. };
  732. // 删除mainWorkOrderBom.value中的maintainedFlag, materialCount字段
  733. const orderBom = processBomData([
  734. ...JSON.parse(JSON.stringify(mainWorkOrderBom.value)),
  735. ]);
  736. console.log("orderBom", orderBom);
  737. // 获取所有物料列表,删除bomName,isNew,bomDisabled字段
  738. const mainWorkOrderMaterials = getAllMaterialList();
  739. const orderMaterials = processMaterialData([...mainWorkOrderMaterials]);
  740. fillWorkOrder({
  741. mainWorkOrder: mainWorkOrder.value,
  742. mainWorkOrderBom: orderBom,
  743. mainWorkOrderMaterials: orderMaterials,
  744. })
  745. .then((res) => {
  746. console.log("fillWorkOrder", res);
  747. if (res.code == 0) {
  748. uni.showToast({
  749. title: t("operation.success"),
  750. icon: "none",
  751. });
  752. setTimeout(() => {
  753. uni.navigateBack();
  754. }, 2000);
  755. } else {
  756. uni.showToast({
  757. title: res.msg,
  758. icon: "none",
  759. });
  760. }
  761. })
  762. .catch((err) => {
  763. console.log("err", err);
  764. });
  765. };
  766. // ----------------------------工单详情初始化---------------------------------
  767. const workOrderDetailInit = async (id) => {
  768. // 获取工单信息
  769. const getMaintenanceDetailAsync = getMaintenanceDetail({ id: id });
  770. mainWorkOrder.value = (await getMaintenanceDetailAsync).data;
  771. // 获取保养项信息
  772. const getWorkOrderBOMsAsync = getWorkOrderBOMs({ workOrderId: id });
  773. const data = (await getWorkOrderBOMsAsync).data;
  774. if (!data.length) return;
  775. // 备份保养项列表原数据(提交用:不修改deviceBomMaterials)
  776. mainWorkOrderBomOriginal.value = JSON.parse(JSON.stringify(data));
  777. //组合mainWorkOrderBom,合并保养项物料列表
  778. data.forEach((bom) => {
  779. // 合并保养项物料列表
  780. const bomKey = bom.bomNodeId;
  781. if (materialByWorkOrderIdKeyAndList.value[bomKey]) {
  782. bom.deviceBomMaterials = materialByWorkOrderIdKeyAndList.value[bomKey];
  783. }
  784. // 若保养项物料列表为空,则初始化空数组
  785. if (!bom.deviceBomMaterials) {
  786. bom.deviceBomMaterials = [];
  787. }
  788. // 若保养项不在保养项列表中,则添加到保养项列表
  789. if (
  790. !mainWorkOrderBom.value.some(
  791. (item) => item.bomNodeId === bom.bomNodeId && item.id === bom.id
  792. )
  793. ) {
  794. mainWorkOrderBom.value.push({
  795. ...bom,
  796. materialCount: 0,
  797. });
  798. }
  799. // 将保养项名称添加到对应的物料列表中
  800. if (bom.deviceBomMaterials) {
  801. bom.deviceBomMaterials.forEach((material) => {
  802. // 为物料添加保养项名称
  803. material.bomName = bom.name;
  804. // 为物料添加是否消耗物料标识(rule=0时为消耗物料,1时为不消耗物料)
  805. material.bomDisabled = bom.rule == 1;
  806. // 确保物料名称和物料编码不为空 且 materialName/name和materialCode/code保持一致
  807. material.materialName = material.materialName
  808. ? material.materialName
  809. : material.name;
  810. material.name = material.materialName;
  811. material.materialCode = material.materialCode
  812. ? material.materialCode
  813. : material.code;
  814. material.code = material.materialCode;
  815. });
  816. }
  817. });
  818. // 默认选中第一个保养项
  819. currentBomData.value = mainWorkOrderBom.value[0];
  820. // 更新传递给子组件的物料列表
  821. materialList.value = currentBomData.value.deviceBomMaterials || [];
  822. // 初始化统计数据
  823. updateBomStatistics();
  824. };
  825. onLoad(async (option) => {
  826. console.log("onLoad", option);
  827. // 通过userId刷新token
  828. await reloginByUserId(option.reloginUserId);
  829. // 查询保养工单已经选择的所有物料
  830. await getBomsMaterialList(option.id);
  831. // 初始化工单详情
  832. await workOrderDetailInit(option.id);
  833. });
  834. onReady(() => {
  835. console.log("onReady");
  836. });
  837. onMounted(() => {
  838. console.log("onMounted");
  839. });
  840. onBackPress(() => {
  841. console.log("onBackPress");
  842. });
  843. </script>
  844. <style lang="scss" scoped>
  845. @import "@/style/work-order-segmented.scss";
  846. .page {
  847. padding-bottom: 0;
  848. }
  849. </style>