taskReport.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. <template>
  2. <div class="ele-body">
  3. <el-form
  4. ref="form"
  5. :model="form"
  6. :rules="rules"
  7. label-position="right"
  8. label-width="110px"
  9. >
  10. <el-row>
  11. <headerTitle style="margin-top: 15px" title="报工信息"></headerTitle>
  12. <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="12">
  13. <el-form-item
  14. :rules="{
  15. required: true,
  16. message: '请选择实际开始时间',
  17. trigger: 'change'
  18. }"
  19. label="实际开始时间"
  20. prop="realStartTime"
  21. >
  22. <el-date-picker
  23. v-model="form.realStartTime"
  24. :disabled="inputDis"
  25. class="time_select"
  26. placeholder="选择实际开始时间"
  27. type="datetime"
  28. value-format="yyyy-MM-dd HH:mm:ss"
  29. @change="handleTimeChange"
  30. ></el-date-picker>
  31. </el-form-item>
  32. </el-col>
  33. <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="12">
  34. <el-form-item
  35. :rules="{
  36. required: true,
  37. message: '请选择实际结束时间',
  38. trigger: 'change'
  39. }"
  40. label="实际结束时间"
  41. prop="realEndTime"
  42. >
  43. <el-date-picker
  44. v-model="form.realEndTime"
  45. :disabled="inputDis"
  46. class="time_select"
  47. placeholder="选择实际结束时间"
  48. type="datetime"
  49. value-format="yyyy-MM-dd HH:mm:ss"
  50. @change="handleTimeChange"
  51. ></el-date-picker>
  52. </el-form-item>
  53. </el-col>
  54. <!-- <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="12">
  55. <el-form-item label="合格数量" prop="qualifiedQuantity">
  56. <el-input
  57. v-model.number="form.qualifiedQuantity"
  58. :disabled="inputDis"
  59. placeholder="请输入"
  60. type="number"
  61. @input="(e) => handleQuantityInput(e, 'qualifiedQuantity')"
  62. ></el-input>
  63. </el-form-item>
  64. </el-col> -->
  65. <!-- <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="12">
  66. <el-form-item label="不合格数量" prop="unqualifiedQuantity">
  67. <el-input
  68. v-model.number="form.unqualifiedQuantity"
  69. :disabled="inputDis"
  70. placeholder="请输入"
  71. type="number"
  72. @input="(e) => handleQuantityInput(e, 'unqualifiedQuantity')"
  73. ></el-input>
  74. </el-form-item>
  75. </el-col> -->
  76. <!-- <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="12">
  77. <el-form-item label="合格重量" prop="qualifiedWeight">
  78. <el-input
  79. v-model.number="form.qualifiedWeight"
  80. :disabled="inputDis"
  81. placeholder="请输入"
  82. type="number"
  83. @input="(e) => handleWeightInput(e, 'qualifiedWeight')"
  84. ></el-input>
  85. </el-form-item>
  86. </el-col> -->
  87. <!-- <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="12">
  88. <el-form-item label="不合格重量" prop="unqualifiedWeight">
  89. <el-input
  90. v-model.number="form.unqualifiedWeight"
  91. :disabled="inputDis"
  92. placeholder="请输入"
  93. type="number"
  94. @input="(e) => handleWeightInput(e, 'unqualifiedWeight')"
  95. ></el-input>
  96. </el-form-item>
  97. </el-col> -->
  98. <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="12">
  99. <el-form-item label="报工数" prop="reportQuantity">
  100. <el-input
  101. v-model.number="form.reportQuantity"
  102. :disabled="inputDis"
  103. placeholder="请输入"
  104. @input="(e) => handleQuantityInput(e, 'reportQuantity')"
  105. ></el-input>
  106. </el-form-item>
  107. </el-col>
  108. <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="12">
  109. <el-form-item label="损耗数" prop="lossQuantity">
  110. <el-input
  111. v-model.number="form.lossQuantity"
  112. :disabled="inputDis"
  113. placeholder="请输入"
  114. @input="(e) => handleQuantityInput(e, 'lossQuantity')"
  115. ></el-input>
  116. </el-form-item>
  117. </el-col>
  118. <el-col :span="24">
  119. <el-form-item label="备注信息">
  120. <el-input
  121. v-model="form.remark"
  122. :disabled="inputDis"
  123. :rows="2"
  124. placeholder="请输入"
  125. type="textarea"
  126. ></el-input>
  127. </el-form-item>
  128. </el-col>
  129. </el-row>
  130. <el-row class="tool_btn">
  131. <el-button type="primary" @click="submitAdd" :loading="loadingBtn"
  132. >报工</el-button
  133. >
  134. </el-row>
  135. <headerTitle style="margin-top: 15px" title="工单信息"></headerTitle>
  136. <el-row :gutter="10" class="basic">
  137. <el-col
  138. v-for="item in fieldMap.orderList"
  139. :key="item.prop"
  140. :lg="8"
  141. :md="12"
  142. :sm="12"
  143. :xl="8"
  144. :xs="12"
  145. >
  146. <el-form-item :label="item.label" class="form80">
  147. <el-input v-model="current[item.prop]" disabled>
  148. <template slot="append" v-if="item.label == '要求生产数量:'">
  149. {{ current['measuringUnit'] }}
  150. </template>
  151. <template slot="append" v-if="item.label == '要求生产重量:'">
  152. {{ current['newWeightUnit'] }}
  153. </template>
  154. </el-input>
  155. </el-form-item>
  156. </el-col>
  157. </el-row>
  158. <headerTitle style="margin-top: 15px" title="任务信息"></headerTitle>
  159. <el-row :gutter="10" class="basic">
  160. <el-col
  161. v-for="item in fieldMap.taskList"
  162. :key="item.prop"
  163. :lg="8"
  164. :md="12"
  165. :sm="12"
  166. :xl="8"
  167. :xs="12"
  168. >
  169. <el-form-item :label="item.label" class="form80">
  170. <el-input v-model="current[item.prop]" disabled>
  171. <template slot="append" v-if="item.label == '任务数量:'">
  172. {{ current['measuringUnit'] }}
  173. </template>
  174. <template slot="append" v-if="item.label == '任务重量:'">
  175. {{ current['newWeightUnit'] }}
  176. </template>
  177. </el-input>
  178. </el-form-item>
  179. </el-col>
  180. </el-row>
  181. <!-- <el-row v-if="title === '详情'">
  182. <headerTitle style="margin-top: 15px" title="报工记录"></headerTitle>
  183. <ModifyRecord ref="ModifyRecordRef" :list="list" />
  184. </el-row> -->
  185. <div slot="footer" class="tool_btn">
  186. <el-button @click="cancel">关闭</el-button>
  187. </div>
  188. </el-form>
  189. </div>
  190. </template>
  191. <script>
  192. import {
  193. listWorkCenter,
  194. batchUpdateRealTime,
  195. listUpdateRealTimeRecord
  196. } from '@/api/workOrderList';
  197. import ModifyRecord from '@/views/taskList/components/ModifyRecord.vue';
  198. export default {
  199. components: {
  200. ModifyRecord
  201. },
  202. props: {
  203. currentRow: {
  204. type: Object,
  205. default: () => ({})
  206. }
  207. },
  208. data() {
  209. return {
  210. title: '详情',
  211. form: {
  212. realEndTime: '',
  213. realStartTime: '',
  214. unqualifiedWeight: '',
  215. unqualifiedQuantity: '',
  216. qualifiedWeight: '',
  217. qualifiedQuantity: '',
  218. remark: '',
  219. reportQuantity: '',
  220. lossQuantity: ''
  221. },
  222. rules: {
  223. // qualifiedQuantity: [
  224. // {
  225. // required: true,
  226. // message: '请输入合格数量',
  227. // trigger: 'blur'
  228. // }
  229. // ],
  230. // qualifiedWeight: [
  231. // {
  232. // required: true,
  233. // message: '请输入合格重量',
  234. // trigger: 'blur'
  235. // }
  236. // ],
  237. // unqualifiedQuantity: [
  238. // {
  239. // required: true,
  240. // message: '请输入不合格数量',
  241. // trigger: 'blur'
  242. // }
  243. // ],
  244. // unqualifiedWeight: [
  245. // {
  246. // required: true,
  247. // message: '请输入不合格重量',
  248. // trigger: 'blur'
  249. // }
  250. // ]
  251. reportQuantity: [
  252. {
  253. required: true,
  254. message: '请输入报工数量',
  255. trigger: 'blur'
  256. }
  257. ],
  258. lossQuantity: [
  259. {
  260. required: true,
  261. message: '请输入损耗数量',
  262. trigger: 'blur'
  263. }
  264. ]
  265. },
  266. current: {},
  267. detailsVisible: false,
  268. fieldMap: {
  269. orderList: [
  270. { label: '计划批次号:', prop: 'batchNo' },
  271. { label: '工单编码:', prop: 'mesWorkOrderCode' },
  272. { label: '编码', prop: 'productCode' },
  273. { label: '名称', prop: 'productName' },
  274. { label: '规格', prop: 'specification' },
  275. { label: '生产订单编码:', prop: 'workOrderCode' },
  276. { label: '计划编号:', prop: 'productionPlanCode' },
  277. { label: '工艺路线:', prop: 'produceRoutingName' },
  278. { label: '要求生产数量:', prop: 'formingNum' },
  279. { label: '要求生产重量:', prop: 'formingWeight' },
  280. { label: '所属工厂:', prop: 'factoryName' },
  281. { label: '所属班组:', prop: 'assignTeamName' },
  282. { label: '计划开始时间:', prop: 'planStartTime' },
  283. { label: '计划结束时间:', prop: 'planCompleteTime' }
  284. ],
  285. taskList: [
  286. { label: '任务编码:', prop: 'assignCode' },
  287. { label: '工序名称:', prop: 'taskName' },
  288. { label: '执行类型:', prop: 'assigneeType' },
  289. { label: '执行对象名称:', prop: 'assigneeName' },
  290. { label: '工时:', prop: 'durationText' },
  291. { label: '任务开始时间:', prop: 'startTime' },
  292. { label: '任务结束时间:', prop: 'endTime' },
  293. { label: '任务数量:', prop: 'quantity' },
  294. { label: '任务重量:', prop: 'weight' }
  295. ]
  296. },
  297. loadingBtn: false,
  298. list: [],
  299. inputDis: false,
  300. reportNum: '',
  301. lossNum: ''
  302. };
  303. },
  304. computed: {
  305. // inputDis() {
  306. // return this.title === '详情';
  307. // }
  308. },
  309. methods: {
  310. open(type, row, form) {
  311. this.title = type === 'report' ? '报工' : '详情';
  312. this.detailsVisible = true;
  313. this.current = { ...row };
  314. if (form.realEndTime) {
  315. this.$nextTick(() => {
  316. this.form = { ...form };
  317. });
  318. if (type !== 'report') {
  319. listUpdateRealTimeRecord(row.apsAssigneeId)
  320. .then((res) => {
  321. if (res) {
  322. this.list = res;
  323. }
  324. })
  325. .catch((err) => {
  326. this.$message.error(err.message);
  327. });
  328. }
  329. }
  330. this.reportNum = Number(this.current.reportQuantityReported) || 0;
  331. this.lossNum = Number(this.current.lossQuantityReported) || 0;
  332. this.getData();
  333. },
  334. cancel() {
  335. this.form = {
  336. realEndTime: '',
  337. realStartTime: '',
  338. unqualifiedWeight: '',
  339. unqualifiedQuantity: '',
  340. qualifiedWeight: '',
  341. qualifiedQuantity: '',
  342. remark: '',
  343. reportQuantity: '',
  344. lossQuantity: ''
  345. };
  346. this.$refs.form.resetFields();
  347. this.detailsVisible = false;
  348. },
  349. async getData() {
  350. try {
  351. const res = await listWorkCenter(this.current.firstTaskId);
  352. this.$set(this.current, 'factoryName', res[0].factoryName);
  353. } catch (err) {
  354. this.$message.warning(err.message);
  355. }
  356. },
  357. // 时间选择
  358. handleTimeChange() {
  359. // 如果结束时间早于开始时间,清空结束时间
  360. if (this.form.realStartTime && this.current.startTime) {
  361. const realStartTime = new Date(this.form.realStartTime).getTime();
  362. const realEndTime = new Date(this.current.startTime).getTime();
  363. if (realStartTime < realEndTime) {
  364. this.form.realStartTime = '';
  365. this.$message.warning('实际开始时间不能早于任务开始时间');
  366. }
  367. }
  368. if (this.form.realStartTime && this.form.realEndTime) {
  369. const realStartTime = new Date(this.form.realStartTime).getTime();
  370. const realEndTime = new Date(this.form.realEndTime).getTime();
  371. if (realStartTime > realEndTime) {
  372. this.form.realEndTime = '';
  373. this.$message.warning('结束时间不能早于开始时间');
  374. }
  375. }
  376. },
  377. // 重量正则判断
  378. handleWeightInput(e, type) {
  379. // 过滤非数字和非小数点字符(包括负号)
  380. let value = e.replace(/[^\d.]/g, '');
  381. // 限制只能有一个小数点
  382. const dotCount = (value.match(/\./g) || []).length;
  383. if (dotCount > 1) {
  384. value = value.slice(0, value.lastIndexOf('.'));
  385. }
  386. // 限制不能以小数点开头
  387. if (value.startsWith('.')) {
  388. value = '0' + value;
  389. }
  390. // 更新绑定值
  391. this.form[type] = value;
  392. // this.calculateWeight(type);
  393. },
  394. //计算重量
  395. calculateWeight(type) {
  396. let total =
  397. this.form.qualifiedWeight - 0 + (this.form.unqualifiedWeight - 0);
  398. if (total > this.current.weight) {
  399. this.form[type] = 0;
  400. this.$message.warning('合格重量加不合格重量不能大于任务重量');
  401. }
  402. },
  403. getDecimalLength(num) {
  404. return (num.toString().split('.')[1] || '').length;
  405. },
  406. toInteger(num) {
  407. const len = this.getDecimalLength(num);
  408. return {
  409. int: Math.round(num * Math.pow(10, len)),
  410. factor: Math.pow(10, len)
  411. };
  412. },
  413. add(a, b) {
  414. const { int: aInt, factor: aFactor } = this.toInteger(a);
  415. const { int: bInt, factor: bFactor } = this.toInteger(b);
  416. const maxFactor = Math.max(aFactor, bFactor);
  417. return (
  418. (aInt * (maxFactor / aFactor) + bInt * (maxFactor / bFactor)) /
  419. maxFactor
  420. );
  421. },
  422. sub(a, b) {
  423. const { int: aInt, factor: aFactor } = this.toInteger(a);
  424. const { int: bInt, factor: bFactor } = this.toInteger(b);
  425. const maxFactor = Math.max(aFactor, bFactor);
  426. return (
  427. (aInt * (maxFactor / aFactor) - bInt * (maxFactor / bFactor)) /
  428. maxFactor
  429. );
  430. },
  431. // 计算数量
  432. calculateQuantity(type) {
  433. const taskQuantity = Number(this.current.quantity) || 0;
  434. const actualQuantity = this.add(this.reportNum, this.lossNum);
  435. const remainingQuantity = this.sub(taskQuantity, actualQuantity);
  436. const reportQuantity = Number(this.form.reportQuantity) || 0;
  437. const lossQuantity = Number(this.form.lossQuantity) || 0;
  438. const total = this.add(reportQuantity, lossQuantity);
  439. if (total > remainingQuantity) {
  440. this.form[type] = '';
  441. this.$message.warning(
  442. `本次报工数与损耗数之和不能超过任务剩余可报数量(剩余 ${remainingQuantity})`
  443. );
  444. }
  445. },
  446. // 数量正则 quantity
  447. handleQuantityInput(e, type) {
  448. // // 过滤非数字字符(包括负号)
  449. // let value = e.replace(/[^\d]/g, '');
  450. // // 限制不能以 0 开头(除非是 0 本身)
  451. // if (value.startsWith('0') && value.length > 1) {
  452. // value = value.slice(1);
  453. // }
  454. // // 更新绑定值
  455. // this.form[type] = value;
  456. let newVal = e.replace(/[^\d.]/g, '');
  457. if (newVal.startsWith('.')) {
  458. newVal = '';
  459. }
  460. const firstDotIndex = newVal.indexOf('.');
  461. if (firstDotIndex !== -1) {
  462. newVal =
  463. newVal.slice(0, firstDotIndex + 1) +
  464. newVal.slice(firstDotIndex + 1).replace(/\./g, '');
  465. }
  466. const match = newVal.match(/^(\d+)(\.\d{0,4})?/);
  467. // this.batchForm.quantity = match ? match[0] : '';
  468. // 更新绑定值
  469. this.form[type] = match ? match[0] : '';
  470. this.calculateQuantity(type);
  471. },
  472. submitAdd() {
  473. this.$refs.form.validate((valid) => {
  474. if (!valid) return;
  475. const taskQuantity = Number(this.current.quantity) || 0;
  476. const actualQuantity = this.add(this.reportNum, this.lossNum);
  477. const remainingQuantity = this.sub(taskQuantity, actualQuantity);
  478. const reportQuantity = Number(this.form.reportQuantity) || 0;
  479. const lossQuantity = Number(this.form.lossQuantity) || 0;
  480. const total = this.add(reportQuantity, lossQuantity);
  481. if (total > remainingQuantity) {
  482. return this.$message.warning(
  483. `本次报工数与损耗数之和不能超过任务剩余可报数量(剩余 ${remainingQuantity})`
  484. );
  485. }
  486. let data = {
  487. ...this.form,
  488. apsAssigneeId: this.current.apsAssigneeId
  489. };
  490. this.loadingBtn = true;
  491. batchUpdateRealTime([data])
  492. .then((res) => {
  493. this.loadingBtn = false;
  494. if (res) {
  495. this.$message.success('报工成功');
  496. this.cancel();
  497. this.$emit('success');
  498. }
  499. })
  500. .catch((err) => {
  501. this.loadingBtn = false;
  502. this.$message.error(err.message);
  503. });
  504. });
  505. }
  506. }
  507. };
  508. </script>
  509. <style scoped lang="scss">
  510. ::v-deep .time_select {
  511. width: 100%;
  512. }
  513. ::v-deep .tool_btn {
  514. text-align: right;
  515. .el-button {
  516. padding: 10px 18px !important;
  517. font-size: 15px !important;
  518. }
  519. }
  520. </style>