workOrderReport.vue 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140
  1. <template>
  2. <u-popup
  3. :show="visible"
  4. :round="0"
  5. :closeOnClickOverlay="false"
  6. :zIndex="99999"
  7. @close="closePopup"
  8. class="u-popup-my"
  9. >
  10. <view class="popup-content">
  11. <view class="popup-header">
  12. <text class="popup-title">{{ title }}</text>
  13. <view class="close-btn" @click="closePopup">×</view>
  14. </view>
  15. <scroll-view class="popup-body" scroll-y>
  16. <view class="page">
  17. <view class="card-a">
  18. <!-- 头部 -->
  19. <view class="a-header">
  20. <text class="a-main-title">{{ form.ruleName }}</text>
  21. <view class="a-sub">
  22. <text
  23. >📋
  24. {{
  25. getDictValue("记录规则类型", form.recordRulesClassify)
  26. }}</text
  27. >
  28. </view>
  29. </view>
  30. <!-- 基本信息网格 -->
  31. <view class="info-grid">
  32. <view class="info-item">
  33. <text class="info-label">检查时间</text>
  34. <uni-datetime-picker
  35. type="datetime"
  36. v-model="form.checkStartTime"
  37. :disabled="title == '详情'"
  38. >
  39. <view class="info-value">{{
  40. form.checkStartTime || "请选择"
  41. }}</view>
  42. </uni-datetime-picker>
  43. </view>
  44. <view class="info-item">
  45. <text class="info-label">报工时间</text>
  46. <uni-datetime-picker
  47. type="datetime"
  48. v-model="form.checkFinishTime"
  49. :disabled="title == '详情'"
  50. >
  51. <view class="info-value">{{
  52. form.checkFinishTime || "请选择"
  53. }}</view>
  54. </uni-datetime-picker>
  55. </view>
  56. <view class="info-item" v-if="!form.type">
  57. <text class="info-label">部门</text>
  58. <view
  59. class="info-value"
  60. :class="{ disabled: title == '详情' }"
  61. @click="title != '详情' && $refs.treePicker._show()"
  62. >{{ form.groupName || "请选择" }}</view
  63. >
  64. </view>
  65. <view class="info-item" v-if="!form.type">
  66. <text class="info-label">指定负责人</text>
  67. <view
  68. class="info-value"
  69. :class="{ disabled: title == '详情' }"
  70. @click="title != '详情' && $refs.selector.open()"
  71. >{{
  72. form.executeUsers.map((item) => item.userName).toString() ||
  73. "请选择"
  74. }}</view
  75. >
  76. </view>
  77. <view class="info-item" v-if="form.type">
  78. <text class="info-label">班组</text>
  79. <view class="info-value">{{ form.teamName }}</view>
  80. </view>
  81. <view class="info-item" v-if="form.type">
  82. <text class="info-label">执行人</text>
  83. <view
  84. class="info-value"
  85. :class="{ disabled: title == '详情' }"
  86. @click="title != '详情' && $refs.selector.open()"
  87. >{{
  88. form.executeUsers.map((item) => item.userName).toString() ||
  89. "请选择"
  90. }}</view
  91. >
  92. </view>
  93. <view class="info-item" v-if="form.type">
  94. <text class="info-label">场站</text>
  95. <view
  96. class="info-value"
  97. :class="{ disabled: title == '详情' }"
  98. @click="title != '详情' && (showProductLineIdPicker = true)"
  99. >
  100. {{ form.productLineName || "请选择" }}
  101. </view>
  102. </view>
  103. <view
  104. class="info-item"
  105. v-if="pageName == 'steamInjectionInspectionRecord'"
  106. >
  107. <text class="info-label">甲方检查人</text>
  108. <view
  109. class="info-value"
  110. :class="{ disabled: title == '详情' }"
  111. @click="title != '详情' && selectContactShow(17)"
  112. >{{ form.contactName || "请选择" }}</view
  113. >
  114. </view>
  115. <view class="info-item" v-if="pageName == 'solidWasteRecord'">
  116. <text class="info-label">处理方</text>
  117. <view
  118. class="info-value"
  119. :class="{ disabled: title == '详情' }"
  120. @click="title != '详情' && selectContactShow(19)"
  121. >{{ form.supplierName || "请选择" }}</view
  122. >
  123. </view>
  124. <view class="info-item" v-if="pageName == 'qualityTestRecords'">
  125. <text class="info-label">联合站检查人</text>
  126. <view
  127. class="info-value"
  128. :class="{ disabled: title == '详情' }"
  129. @click="title != '详情' && selectContactShow(17)"
  130. >{{ form.contactName || "请选择" }}</view
  131. >
  132. </view>
  133. </view>
  134. <!-- 运行参数记录区 -->
  135. <view class="records-area">
  136. <view class="section-header">
  137. <view class="section-title">
  138. <text>📊 记录数据</text>
  139. </view>
  140. </view>
  141. <!-- 批量操作按钮放在标题下方 -->
  142. <view
  143. class="batch-bar"
  144. v-if="pageName != 'productionRecords' && title != '详情'"
  145. >
  146. <button class="btn btn-primary" @click="batchCheck">
  147. ✅ 批量检查
  148. </button>
  149. <button class="btn btn-secondary" @click="batchQualified">
  150. 🏭 批量合格
  151. </button>
  152. </view>
  153. <!-- 检查项列表 -->
  154. <view
  155. v-for="(item, idx) in form.detailList"
  156. :key="idx"
  157. class="check-item-a"
  158. >
  159. <view class="item-row1">
  160. <view class="item-info">
  161. <text class="item-name"
  162. >{{ idx + 1 }}. {{ item.paramValue }}
  163. {{ item.unitName ? "(" + item.unitName + ")" : "" }}</text
  164. >
  165. </view>
  166. </view>
  167. <view class="item-row-input" v-if="item.substanceName">
  168. <text class="input-label">设备</text>
  169. <view class="item-input"> {{ item.substanceName }}</view>
  170. </view>
  171. <!-- 描述/数量 -->
  172. <view class="item-row-input">
  173. <text class="input-label">{{
  174. pageName == "qualityTestRecords" ? "描述" : "数量"
  175. }}</text>
  176. <input
  177. class="item-input"
  178. v-model="item.num"
  179. type="text"
  180. placeholder="请输入数量"
  181. :disabled="title == '详情'"
  182. />
  183. </view>
  184. <view
  185. class="item-row-input"
  186. v-if="pageName == 'qualityTestRecords'"
  187. >
  188. <text class="input-label">执行人</text>
  189. <view
  190. class="info-value"
  191. style="padding: 6px 10px; flex: 1; border-radius: 28rpx"
  192. :class="{ disabled: title == '详情' }"
  193. @click="title != '详情' && itemExecuteUsers(idx, item)"
  194. >{{
  195. (item.checkUsers &&
  196. item.checkUsers
  197. .map((item) => item.userName)
  198. .toString()) ||
  199. "请选择"
  200. }}</view
  201. >
  202. </view>
  203. <view
  204. class="item-actions"
  205. v-if="pageName != 'productionRecords'"
  206. >
  207. <u-radio-group
  208. class="result-group"
  209. v-model="item.checkStatus"
  210. :disabled="title == '详情'"
  211. >
  212. <u-radio label="已检查" :name="1"></u-radio>
  213. <u-radio label="未检查" :name="0"></u-radio>
  214. </u-radio-group>
  215. <u-radio-group
  216. class="result-group"
  217. v-model="item.checkResult"
  218. :disabled="title == '详情'"
  219. >
  220. <u-radio label="合格" :name="1"></u-radio>
  221. <u-radio label="不合格" :name="0"></u-radio>
  222. </u-radio-group>
  223. </view>
  224. </view>
  225. </view>
  226. </view>
  227. </view>
  228. </scroll-view>
  229. <view class="popup-footer">
  230. <u-button type="default" @click="closePopup">关闭</u-button>
  231. <u-button
  232. type="primary"
  233. @click="submit()"
  234. v-if="title == '报工'"
  235. :loading="loading"
  236. >提交</u-button
  237. >
  238. </view>
  239. </view>
  240. <u-toast ref="uToast"></u-toast>
  241. <ba-tree-picker
  242. ref="treePicker"
  243. key="verify"
  244. :multiple="false"
  245. @select-change="searchDeptNodeClick"
  246. title="选择部门"
  247. :localdata="classificationList"
  248. valueKey="id"
  249. textKey="name"
  250. childrenKey="children"
  251. />
  252. <search-select
  253. :multiple="true"
  254. ref="selector"
  255. v-model="form.executeUsersIds"
  256. :data-list="
  257. executorList.map((val) => {
  258. return {
  259. text: val.name,
  260. value: val.id,
  261. };
  262. })
  263. "
  264. title="选择"
  265. @confirm="executeIdListChange"
  266. >
  267. </search-select>
  268. <!-- 场站 -->
  269. <u-picker
  270. :show="showProductLineIdPicker"
  271. keyName="label"
  272. :columns="[
  273. productLineList.map((item) => {
  274. return {
  275. label: item.name,
  276. value: item.id,
  277. };
  278. }),
  279. ]"
  280. @confirm="confirmProductLine"
  281. @cancel="showProductLineIdPicker = false"
  282. ></u-picker>
  283. <search-select
  284. :multiple="true"
  285. ref="selector1"
  286. v-model="checkUsersIds"
  287. :data-list="
  288. (form.executeUsers &&
  289. form.executeUsers.map((val) => {
  290. return {
  291. text: val.userName,
  292. value: val.userId,
  293. };
  294. })) ||
  295. []
  296. "
  297. title="选择"
  298. @confirm="executeIdListChange1"
  299. >
  300. </search-select>
  301. <!-- <u-picker
  302. :show="showCheckUsersIds"
  303. keyName="label"
  304. mode="multiSelector"
  305. :columns="[
  306. form.executeUsers &&
  307. form.executeUsers.map((item) => {
  308. return {
  309. label: item.userName,
  310. value: item.userId,
  311. };
  312. }),
  313. ]"
  314. @confirm="confirmCheckUsersIds"
  315. @cancel="showCheckUsersIds = false"
  316. ></u-picker> -->
  317. </u-popup>
  318. </template>
  319. <script>
  320. const formBaseData = {
  321. id: null,
  322. workshopArea: "",
  323. workshopAreaId: null,
  324. checkFinishTime: "",
  325. checkStartTime: "",
  326. checkValidity: null,
  327. checkValidityUnit: "",
  328. conclusion: null,
  329. detailList: [],
  330. deviceId: 0,
  331. deviceName: "",
  332. batchNo: "",
  333. executeMethod: 0,
  334. formingNum: 0,
  335. itemType: 0,
  336. produceRoutingId: 0,
  337. produceRoutingName: "",
  338. produceTaskConfigId: 0,
  339. produceTaskId: 0,
  340. produceTaskName: "",
  341. productCode: "",
  342. productModel: "",
  343. productName: "",
  344. recordRulesClassify: null,
  345. recordTemplateStyle: null,
  346. ruleId: 0,
  347. ruleName: "",
  348. reportWorkType: 0,
  349. specification: "",
  350. workOrderCode: "",
  351. workOrderId: 0,
  352. itemTaskName: "",
  353. brandNo: "",
  354. duration: null,
  355. // 执行人
  356. executeUsersIds: [],
  357. executeUsers: [],
  358. showProductLineIdPicker: false,
  359. // 班组id
  360. teamId: "",
  361. groupId: null,
  362. // 物料字段name
  363. pickDetails: [],
  364. // 本次产出明细
  365. outputDetails: [],
  366. recordRulesExecuteMethodId: null,
  367. recordRulesExecuteMethodName: "",
  368. // 产出类型 1-原材料,2-在制品,3.BOM标准产出
  369. outputType: 1,
  370. type: 0,
  371. contactName: "",
  372. contactId: "",
  373. supplierId: "",
  374. supplierName: "",
  375. productLineId: null,
  376. productLineName: "",
  377. };
  378. import {
  379. getById,
  380. producetaskrulerecordSaveOrUpdateAndSubmit,
  381. } from "@/api/recordRules/index";
  382. import { getTeamPage } from "@/api/pda/workOrderHandover.js";
  383. import { getUserPage, listOrganizations } from "@/api/common.js";
  384. import { toTreeData } from "@/utils/utils.js";
  385. import { mapGetters } from "vuex";
  386. import searchSelect from "@/pages/salesServiceManagement/accessory/components/searchSelect.vue";
  387. export default {
  388. data() {
  389. return {
  390. visible: false,
  391. executeUsersIds: [],
  392. title: "",
  393. loading: false,
  394. butLoading: false,
  395. checked: false,
  396. teamUserList: [],
  397. teamList: [],
  398. teamAllList: [],
  399. classificationList: [],
  400. showProductLineIdPicker: false,
  401. showCheckUsersIds: false,
  402. executorList: [],
  403. allTeamList: [],
  404. productLineList: [],
  405. currentIndex: "",
  406. form: JSON.parse(JSON.stringify(formBaseData)),
  407. };
  408. },
  409. computed: {
  410. ...mapGetters(["getDictValue"]),
  411. },
  412. components: {
  413. searchSelect,
  414. },
  415. props: {
  416. pageName: "",
  417. },
  418. methods: {
  419. async open(row, type) {
  420. uni.$off("setSelectList");
  421. uni.$on("setSelectList", (data) => {
  422. console.log(data, "data");
  423. if (data && data.length > 0) {
  424. this.form.contactName = data[0].name;
  425. this.form.contactId = data[0].id;
  426. this.form.supplierName = data[0].name;
  427. this.form.supplierId = data[0].id;
  428. }
  429. });
  430. if (type == "edit") {
  431. this.title = "报工";
  432. } else {
  433. this.title = "详情";
  434. }
  435. this.getTreeList();
  436. await this.getOrderDetials(row.id);
  437. this.visible = true;
  438. },
  439. cancel() {
  440. this.form = {
  441. ...JSON.parse(JSON.stringify(formBaseData)),
  442. };
  443. this.visible = false;
  444. },
  445. async getTreeList() {
  446. const data = await listOrganizations({});
  447. let treeList = toTreeData({
  448. data: data || [],
  449. idField: "id",
  450. parentIdField: "parentId",
  451. });
  452. this.classificationList = treeList;
  453. },
  454. selectContactShow(type) {
  455. uni.navigateTo({
  456. url:
  457. "/pages/saleManage/components/selectContact?isAll=" +
  458. false +
  459. "&contactType=" +
  460. type,
  461. });
  462. },
  463. confirmProductLine(e) {
  464. this.form.productLineId = e.value[0].value;
  465. this.form.productLineName = e.value[0].label;
  466. this.showProductLineIdPicker = false;
  467. },
  468. closePopup() {
  469. this.cancel();
  470. },
  471. handParent() {
  472. this.$refs.parentListRef.open();
  473. },
  474. changeParent(data) {
  475. this.form.contactName = data.name;
  476. this.form.contactId = data.id;
  477. this.form.supplierName = data.name;
  478. this.form.supplierId = data.id;
  479. },
  480. // 获取工单详情
  481. async getOrderDetials(id) {
  482. try {
  483. const data = await getById(id);
  484. data.detailList = data.detailList.map((i) => {
  485. i.toolNames = i.tools.map((j) => j.toolName).join(",");
  486. if (i.checkUsers && i.checkUsers.length > 0) {
  487. console.log("i.checkUsers", i.checkUsers);
  488. i.checkUsersIds = i.checkUsers.map((j) => j.userId);
  489. }
  490. if (
  491. (!i.checkUsersIds || i.checkUsersIds.length == 0) &&
  492. data.executeUsers.length > 0
  493. ) {
  494. // 默认执行人作为检查人
  495. i.checkUsersIds = data.executeUsers
  496. .filter((i) => i.userId)
  497. .map((j) => j.userId);
  498. i.checkUsers = data.executeUsers
  499. .filter((i) => i.userId)
  500. .map((j) => {
  501. return {
  502. teamId: j.teamId,
  503. teamName: j.teamName,
  504. userId: j.userId,
  505. userName: j.userName,
  506. };
  507. });
  508. }
  509. return i;
  510. });
  511. // this.$util.assignObject(this.form, data);
  512. Object.assign(this.form, data);
  513. this.form.executeUsersIds = this.form.executeUsers
  514. .map((i) => i.userId)
  515. .filter((i) => i); // 过滤掉 undefined
  516. if (this.form.executeUsers.length > 0 && !this.form.type) {
  517. this.form.groupId = this.form.executeUsers[0]?.groupId + "";
  518. this.form.groupName = this.form.executeUsers[0]?.groupName;
  519. if (this.form.groupId) {
  520. this.searchDeptNodeClick(
  521. this.form.groupId,
  522. this.form.groupName,
  523. "init",
  524. );
  525. }
  526. }
  527. if (!this.form.teamId && this.teamList.length > 0) {
  528. this.form.teamId = this.teamList[0].id;
  529. }
  530. this.form.recordRulesClassify += "";
  531. // 加载班组人员列表
  532. if (this.form.teamId && this.form.type) {
  533. await this.getAllTeamList();
  534. const index = this.allTeamList.findIndex(
  535. (item) => item.id == this.form.teamId,
  536. );
  537. this.executorList = this.allTeamList[index].userVOList;
  538. this.productLineList = [];
  539. this.allTeamList[index].factoryWorkstationVOList.forEach((item) => {
  540. if (
  541. !this.productLineList.find((p) => p.id === item.productionLineId)
  542. ) {
  543. this.productLineList.push({
  544. name: item.productionLineName,
  545. id: item.productionLineId,
  546. });
  547. }
  548. });
  549. }
  550. this.$nextTick(() => {
  551. this.loading = false;
  552. });
  553. } catch (error) {
  554. console.log("error", error);
  555. this.loading = false;
  556. }
  557. },
  558. checkTeamList(id) {
  559. const index = this.teamList.findIndex((item) => item.id == id);
  560. this.teamUserList = this.teamAllList[index];
  561. this.form.teamName = this.teamList[index].name;
  562. console.log("this.teamUserList", this.teamUserList);
  563. },
  564. // 批量检查
  565. batchCheck() {
  566. this.form.detailList.forEach((i, index) => {
  567. this.$set(this.form.detailList[index], "checkStatus", 1);
  568. });
  569. },
  570. // 批量合格
  571. batchQualified() {
  572. this.form.detailList.forEach((i, index) => {
  573. this.$set(this.form.detailList[index], "checkResult", 1);
  574. });
  575. },
  576. itemExecuteUsers(index, row) {
  577. this.currentIndex = index;
  578. this.$refs.selector1.open();
  579. },
  580. executeIdListChange1() {
  581. this.$set(
  582. this.form.detailList[this.currentIndex],
  583. "checkUsersIds",
  584. this.checkUsersIds,
  585. );
  586. const checkUsersIds = this.checkUsersIds;
  587. this.checkUsersIds = [];
  588. const checkUsers = checkUsersIds.map((i) => {
  589. const user = this.form.executeUsers.find((item) => item.userId === i);
  590. return user;
  591. });
  592. console.log(checkUsers, "checkUsers");
  593. this.$set(
  594. this.form.detailList[this.currentIndex],
  595. "checkUsers",
  596. checkUsers,
  597. );
  598. },
  599. // 获取审核人列表、巡点检人员
  600. async getUserList(params) {
  601. try {
  602. let data = {
  603. pageNum: 1,
  604. size: -1,
  605. };
  606. // 如果传了参数就是获取部门人员数据
  607. if (params) {
  608. data = Object.assign(data, params);
  609. }
  610. const res = await getUserPage(data);
  611. if (params) {
  612. this.executorList = res.list;
  613. }
  614. } catch (error) {}
  615. },
  616. //选择部门(搜索)
  617. async searchDeptNodeClick(id, name, type) {
  618. this.form.groupId = id;
  619. this.form.groupName = name;
  620. if (id) {
  621. // 根据部门获取人员
  622. const params = {
  623. groupId: id,
  624. };
  625. await this.getUserList(params);
  626. } else {
  627. this.form.groupId = null;
  628. }
  629. if (type != "init") {
  630. this.form.detailList.forEach((detail) => {
  631. detail.checkUsersIds = [];
  632. detail.checkUsers = [];
  633. });
  634. this.form.executeUsers = [];
  635. this.form.executeUsersIds = [];
  636. }
  637. },
  638. // 负责人变更 同步执行人列表
  639. executeIdListChange() {
  640. console.log(this.form.executeUsersIds, "this.form.executeUsersIds");
  641. this.form.executeUsers = this.form.executeUsersIds
  642. .map((userId) => {
  643. const user = this.executorList.find((u) => u.id === userId);
  644. if (!user) return null;
  645. return {
  646. userId: user.id,
  647. userName: user.name,
  648. groupId: user.groupId,
  649. groupName: user.groupName,
  650. };
  651. })
  652. .filter((i) => i);
  653. // 同步详情执行人
  654. this.form.detailList.forEach((detail, index) => {
  655. this.$set(
  656. this.form.detailList[index],
  657. "checkUsersIds",
  658. this.form.executeUsersIds,
  659. );
  660. this.$set(
  661. this.form.detailList[index],
  662. "checkUsers",
  663. this.form.executeUsers,
  664. );
  665. });
  666. console.log("this.form.executeUsers", this.form.detailList);
  667. },
  668. async getAllTeamList() {
  669. const { list } = await getTeamPage({
  670. pageNum: 1,
  671. size: -1,
  672. });
  673. console.log("teamAllList 班组", list);
  674. this.allTeamList = list;
  675. },
  676. // 提交
  677. async submit() {
  678. // 验证检查时间和报工时间必填
  679. if (!this.form.checkStartTime) {
  680. this.$refs.uToast.show({
  681. type: "error",
  682. icon: false,
  683. message: `请选择检查时间`,
  684. });
  685. return;
  686. }
  687. if (!this.form.checkFinishTime) {
  688. this.$refs.uToast.show({
  689. type: "error",
  690. icon: false,
  691. message: `请选择报工时间`,
  692. });
  693. return;
  694. }
  695. // 验证报工时间必须大于等于检查时间
  696. const startTime = new Date(this.form.checkStartTime).getTime();
  697. const finishTime = new Date(this.form.checkFinishTime).getTime();
  698. if (finishTime < startTime) {
  699. this.$refs.uToast.show({
  700. type: "error",
  701. icon: false,
  702. message: `报工时间不能早于检查时间`,
  703. });
  704. return;
  705. }
  706. if (this.form.executeUsersIds.length == 0) {
  707. this.$refs.uToast.show({
  708. type: "error",
  709. icon: false,
  710. message: `请选择执行人`,
  711. });
  712. return;
  713. }
  714. // 报工需要验证 缓存不验证 排除 recordTemplateStyle ==3 物料添加 == 4生产统计 模板
  715. if (this.pageName != "productionRecords") {
  716. // 验证检查项目
  717. const detailRequired = this.form.detailList.some((i) => {
  718. return i.checkResult == null || i.checkStatus == null;
  719. });
  720. if (detailRequired) {
  721. this.$refs.uToast.show({
  722. type: "error",
  723. icon: false,
  724. message: `请先完善、检查情况、检查结果`,
  725. });
  726. return;
  727. }
  728. }
  729. // 处理时间格式 - 如果没有时分秒则补齐 00:00:00
  730. const formatTime = (timeStr) => {
  731. if (!timeStr) return timeStr;
  732. // 如果只包含日期部分 (YYYY-MM-DD),添加时分秒
  733. if (/^\d{4}-\d{2}-\d{2}$/.test(timeStr)) {
  734. return timeStr + " 00:00:00";
  735. }
  736. return timeStr;
  737. };
  738. this.form.checkStartTime = formatTime(this.form.checkStartTime);
  739. this.form.checkFinishTime = formatTime(this.form.checkFinishTime);
  740. const body = JSON.parse(JSON.stringify(this.form));
  741. try {
  742. await producetaskrulerecordSaveOrUpdateAndSubmit(body);
  743. this.$emit("refresh");
  744. this.closePopup();
  745. } catch (error) {
  746. console.log(error, "dsdsd");
  747. }
  748. },
  749. },
  750. };
  751. </script>
  752. <style scoped lang="scss">
  753. .popup-content {
  754. width: 100vw;
  755. height: calc(100vh - 100px);
  756. background: #fff;
  757. border-radius: 0;
  758. display: flex;
  759. background-color: #eff2f7;
  760. flex-direction: column;
  761. }
  762. .popup-header {
  763. display: flex;
  764. justify-content: space-between;
  765. align-items: center;
  766. padding: 30rpx;
  767. border-bottom: 1rpx solid #e5e5e5;
  768. .popup-title {
  769. font-size: 36rpx;
  770. font-weight: bold;
  771. color: #333;
  772. }
  773. .close-btn {
  774. width: 60rpx;
  775. height: 60rpx;
  776. display: flex;
  777. align-items: center;
  778. justify-content: center;
  779. font-size: 60rpx;
  780. color: #999;
  781. line-height: 1;
  782. }
  783. }
  784. .popup-body {
  785. flex: 1;
  786. overflow-y: auto;
  787. padding: 28rpx;
  788. }
  789. .page {
  790. font-family:
  791. system-ui,
  792. -apple-system,
  793. "Segoe UI",
  794. Roboto,
  795. Helvetica,
  796. sans-serif;
  797. }
  798. .design-section {
  799. /* margin-bottom: 60rpx; */
  800. }
  801. .design-header {
  802. margin-bottom: 20rpx;
  803. padding-left: 8rpx;
  804. }
  805. .design-title {
  806. font-size: 44rpx;
  807. font-weight: 800;
  808. color: #1f2b3c;
  809. display: block;
  810. }
  811. .design-desc {
  812. font-size: 26rpx;
  813. color: #6b7280;
  814. margin-top: 6rpx;
  815. }
  816. /* 卡片白色风格 */
  817. .card-a {
  818. background: #ffffff;
  819. border-radius: 48rpx;
  820. box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.05);
  821. overflow: hidden;
  822. }
  823. .a-header {
  824. padding: 40rpx 32rpx 24rpx;
  825. border-bottom: 2rpx solid #f0f2f5;
  826. }
  827. .a-main-title {
  828. font-size: 36rpx;
  829. font-weight: 800;
  830. background: linear-gradient(135deg, #1f2b3c, #2c3e50);
  831. background-clip: text;
  832. -webkit-background-clip: text;
  833. color: transparent;
  834. letter-spacing: -0.5rpx;
  835. }
  836. .a-sub {
  837. font-size: 24rpx;
  838. color: #8e9aae;
  839. margin-top: 12rpx;
  840. display: flex;
  841. gap: 24rpx;
  842. }
  843. .info-grid {
  844. padding: 30rpx 32rpx;
  845. display: grid;
  846. grid-template-columns: 1fr 1fr;
  847. gap: 28rpx 20rpx;
  848. // background: #fcfdfe;
  849. border-bottom: 2rpx solid #f0f2f5;
  850. }
  851. .info-item {
  852. display: flex;
  853. flex-direction: column;
  854. gap: 8rpx;
  855. }
  856. .info-label {
  857. font-size: 26rpx;
  858. font-weight: 600;
  859. color: #6c7a91;
  860. text-transform: uppercase;
  861. }
  862. .records-area {
  863. padding: 16rpx 28rpx 32rpx;
  864. }
  865. .section-header {
  866. display: flex;
  867. justify-content: space-between;
  868. align-items: center;
  869. margin: 16rpx 0 20rpx 8rpx;
  870. }
  871. .section-title {
  872. font-size: 36rpx;
  873. font-weight: 700;
  874. color: #1f2a44;
  875. display: flex;
  876. align-items: center;
  877. gap: 16rpx;
  878. }
  879. .badge {
  880. background: #eff3fa;
  881. padding: 6rpx 20rpx;
  882. border-radius: 60rpx;
  883. font-size: 24rpx;
  884. font-weight: normal;
  885. color: #2c5f8a;
  886. }
  887. .device-card-a {
  888. background: #f8fbfe;
  889. border-radius: 36rpx;
  890. padding: 20rpx 24rpx;
  891. margin-bottom: 30rpx;
  892. display: flex;
  893. align-items: center;
  894. justify-content: space-between;
  895. flex-wrap: wrap;
  896. border: 2rpx solid #e9edf2;
  897. }
  898. .device-label {
  899. font-weight: 700;
  900. font-size: 28rpx;
  901. color: #1f2a44;
  902. background: #eff3fa;
  903. padding: 8rpx 24rpx;
  904. border-radius: 60rpx;
  905. }
  906. .device-input-a {
  907. flex: 1;
  908. min-width: 280rpx;
  909. background: #ffffff;
  910. border: 2rpx solid #dce3ec;
  911. border-radius: 36rpx;
  912. padding: 16rpx 24rpx;
  913. font-size: 28rpx;
  914. }
  915. .check-item-a {
  916. background: #ffffff;
  917. border-radius: 36rpx;
  918. padding: 24rpx 24rpx;
  919. margin-bottom: 24rpx;
  920. box-shadow:
  921. 0 4rpx 16rpx rgba(0, 0, 0, 0.02),
  922. 0 0 0 2rpx #edf2f7;
  923. }
  924. .item-row1 {
  925. display: flex;
  926. justify-content: space-between;
  927. align-items: baseline;
  928. flex-wrap: wrap;
  929. margin-bottom: 20rpx;
  930. gap: 16rpx;
  931. }
  932. .item-name {
  933. font-weight: 620;
  934. font-size: 30rpx;
  935. color: #1f2a44;
  936. background: #f5f7fb;
  937. padding: 6rpx 20rpx;
  938. border-radius: 50rpx;
  939. }
  940. .item-info {
  941. display: flex;
  942. flex-direction: column;
  943. gap: 8rpx;
  944. }
  945. .item-row-input {
  946. display: flex;
  947. align-items: center;
  948. gap: 16rpx;
  949. margin-bottom: 20rpx;
  950. padding: 0 8rpx;
  951. }
  952. .input-label {
  953. font-size: 28rpx;
  954. color: #555;
  955. font-weight: 500;
  956. white-space: nowrap;
  957. }
  958. .item-input {
  959. // background: #f8fafe;
  960. border: 2rpx solid #e2e8f0;
  961. border-radius: 36rpx;
  962. padding: 12rpx 24rpx;
  963. font-size: 30rpx;
  964. height: 60rpx;
  965. flex: 1;
  966. font-family: monospace;
  967. }
  968. .item-actions {
  969. display: flex;
  970. justify-content: space-between;
  971. align-items: center;
  972. flex-wrap: wrap;
  973. gap: 20rpx;
  974. }
  975. .check-status,
  976. .result-group {
  977. display: flex;
  978. align-items: center;
  979. gap: 24rpx;
  980. background: #f8fafe;
  981. padding: 8rpx 24rpx;
  982. border-radius: 60rpx;
  983. font-size: 28rpx;
  984. }
  985. /deep/.result-group {
  986. .u-radio__text {
  987. font-size: 28rpx !important;
  988. }
  989. .u-radio__icon-wrap {
  990. width: 30rpx !important;
  991. height: 30rpx !important;
  992. }
  993. }
  994. .radio-label {
  995. display: flex;
  996. align-items: center;
  997. gap: 8rpx;
  998. }
  999. .batch-bar {
  1000. display: flex;
  1001. gap: 16rpx;
  1002. margin: 16rpx 0 20rpx 8rpx;
  1003. }
  1004. .btn {
  1005. flex: 1;
  1006. text-align: center;
  1007. border-radius: 80rpx;
  1008. font-weight: 700;
  1009. font-size: 30rpx;
  1010. border: none;
  1011. }
  1012. .btn-primary {
  1013. background: #1e6f5c;
  1014. color: white;
  1015. box-shadow: 0 4rpx 12rpx rgba(30, 111, 92, 0.2);
  1016. }
  1017. .btn-secondary {
  1018. background: #2c7da0;
  1019. color: white;
  1020. }
  1021. .footnote {
  1022. font-size: 22rpx;
  1023. color: #8e9aab;
  1024. text-align: center;
  1025. margin-top: 16rpx;
  1026. display: block;
  1027. }
  1028. /* 修复组件样式 */
  1029. radio,
  1030. checkbox {
  1031. transform: scale(0.9);
  1032. margin-right: 6rpx;
  1033. }
  1034. button:after {
  1035. border: none;
  1036. }
  1037. .info-value {
  1038. font-size: 28rpx;
  1039. font-weight: 500;
  1040. color: #1e2a3a;
  1041. // background: #f2f5f9;
  1042. padding: 16rpx 20rpx;
  1043. border-radius: 24rpx;
  1044. border: 2rpx solid #e9edf2;
  1045. &.disabled {
  1046. color: #999;
  1047. background: #f5f5f5;
  1048. }
  1049. }
  1050. .popup-footer {
  1051. display: flex;
  1052. padding: 20rpx 30rpx;
  1053. border-top: 1rpx solid #e5e5e5;
  1054. gap: 20rpx;
  1055. /deep/ .u-button {
  1056. flex: 1;
  1057. }
  1058. }
  1059. /deep/.uni-select {
  1060. border-radius: 36rpx;
  1061. }
  1062. </style>