CollectForm.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. <template>
  2. <view>
  3. <cu-custom :backUrl="'/pages/index/index'" :isBack="true" bgColor="bg-gradual-blue">
  4. <block slot="content">领用申请</block>
  5. </cu-custom>
  6. <u--form :model="inputForm" labelWidth="100px" class="u-form" labelPosition="left" :rules="rules"
  7. ref="inputForm">
  8. <u-form-item label="领用编号" borderBottom prop="collectNo">
  9. <u--input placeholder="自动生成" v-model="inputForm.collectNo" disabled></u--input>
  10. </u-form-item>
  11. <u-form-item label="经办人" borderBottom prop="handledBy">
  12. <u--input v-model="inputForm.handledBy" disabled></u--input>
  13. </u-form-item>
  14. <u-form-item label="经办部门" borderBottom prop="handledByOfficeName">
  15. <u--input v-model="inputForm.handledByOfficeName" disabled></u--input>
  16. </u-form-item>
  17. <u-form-item label="领用时间" borderBottom prop="collectDate" :required="true">
  18. <el-date-picker v-model="inputForm.collectDate" type="date" placeholder="请选择领用时间" style="width:100%"
  19. placement="bottom-start" clearable :disabled="nodeFlag">
  20. </el-date-picker>
  21. </u-form-item>
  22. <u-form-item label="备注" borderBottom prop="remarks">
  23. <u--textarea placeholder="请填写备注信息" :rows="4" :maxlength="500" v-model="inputForm.remarks"
  24. :disabled="nodeFlag"></u--textarea>
  25. </u-form-item>
  26. <view class="section-wrap">
  27. <view class="section-title">领用详情</view>
  28. <view class="section-tip"></view>
  29. <view v-for="(item, index) in inputForm.detailInfos" :key="item.id || index" class="detail-card">
  30. <view class="detail-card-title">领用详情 {{ index + 1 }}</view>
  31. <!-- <u-form-item label="领用人" :prop="'detailInfos[' + index + '].recipientAgent'" :required="true">
  32. <u--input v-model="inputForm.detailInfos[index].recipientAgent" placeholder="请选择领用人"
  33. :disabled="nodeFlag" @focus="openUserPullForm(index)" clearable></u--input>
  34. </u-form-item>
  35. <u-form-item label="领用人部门" :prop="'detailInfos[' + index + '].recipientOffice'">
  36. <u--input v-model="inputForm.detailInfos[index].recipientOffice" disabled></u--input>
  37. </u-form-item> -->
  38. <collect-goods-selector ref="goodsSelect" :rowIndex="index" :inputForm="inputForm"
  39. :disabled="nodeFlag" @selected="handleGoodsSelected"></collect-goods-selector>
  40. <u-form-item label="领用数量" :prop="'detailInfos[' + index + '].collectNumber'" :required="true">
  41. <u--input v-model="inputForm.detailInfos[index].collectNumber" placeholder="请输入领用数量"
  42. :disabled="nodeFlag" @blur="handleCollectNumberBlur(index)"></u--input>
  43. </u-form-item>
  44. <u-form-item label="库存数量" :prop="'detailInfos[' + index + '].kc'">
  45. <u--input v-model="inputForm.detailInfos[index].kc" disabled></u--input>
  46. </u-form-item>
  47. <u-form-item label="品牌" :prop="'detailInfos[' + index + '].brand'">
  48. <u--input v-model="inputForm.detailInfos[index].brand" disabled></u--input>
  49. </u-form-item>
  50. <u-form-item label="规格" :prop="'detailInfos[' + index + '].specification'">
  51. <u--input v-model="inputForm.detailInfos[index].specification" disabled></u--input>
  52. </u-form-item>
  53. <u-form-item label="单位" :prop="'detailInfos[' + index + '].company'">
  54. <u--input v-model="inputForm.detailInfos[index].company" placeholder="请输入单位"
  55. :disabled="true"></u--input>
  56. </u-form-item>
  57. <!-- <u-form-item label="包装规格" :prop="'detailInfos[' + index + '].spec'">
  58. <u--input v-model="inputForm.detailInfos[index].spec" disabled></u--input>
  59. </u-form-item> -->
  60. <u-form-item label="备注" :prop="'detailInfos[' + index + '].remarks'">
  61. <u--input v-model="inputForm.detailInfos[index].remarks" placeholder="请输入备注"
  62. :disabled="nodeFlag"></u--input>
  63. </u-form-item>
  64. <u-form-item label="附件上传">
  65. <UploadComponent :uploadUrl="`${uploadUrl}/public-modules-server/oss/file/webUpload/upload`"
  66. @onRemove="(file, fileList, fileIndex) => handleRemove(file, fileList, index, fileIndex, 'detail')"
  67. @onSuccess="(file, fileList) => handleUploadSuccess(file, fileList, index, 'detail')"
  68. :fileList="inputForm.detailInfos[index].fileInfoLost" :limit="3" :isDelete="nodeFlag"
  69. :isUpload="nodeFlag">
  70. </UploadComponent>
  71. </u-form-item>
  72. <u-form-item label="" v-if="!nodeFlag">
  73. <el-button style="width: 100%" type="danger" plain @click="removeDetail(index)">
  74. 删除领用详情
  75. </el-button>
  76. </u-form-item>
  77. </view>
  78. <u-form-item label="" v-if="!nodeFlag">
  79. <el-button style="width: 100%" type="primary" @click="addDetail()" plain>新增领用详情</el-button>
  80. </u-form-item>
  81. </view>
  82. <view class="section-wrap" v-if="false">
  83. <view class="section-title">领用分配详情</view>
  84. <view v-for="(item, index) in inputForm.recordList" :key="item.id || index" class="detail-card">
  85. <view class="detail-card-title">领用分配详情 {{ index + 1 }}</view>
  86. <u-form-item label="领用人" :prop="'recordList[' + index + '].recipientAgent'" :required="true">
  87. <u--input v-model="inputForm.recordList[index].recipientAgent" placeholder="请选择领用人"
  88. :disabled="nodeFlag" @focus="openUserPullForm(index)" clearable></u--input>
  89. </u-form-item>
  90. <u-form-item label="领用人部门" :prop="'recordList[' + index + '].recipientOffice'">
  91. <u--input v-model="inputForm.recordList[index].recipientOffice" disabled></u--input>
  92. </u-form-item>
  93. <u-form-item label="物品名称" :prop="'recordList[' + index + '].collectType'" :required="true">
  94. <u--input v-model="inputForm.recordList[index].collectType" placeholder="请选择物品名称"
  95. :disabled="nodeFlag" @focus="openTypePicker(index)" clearable></u--input>
  96. </u-form-item>
  97. <u-form-item label="物品名称" :prop="'recordList[' + index + '].goodsName'" :required="true">
  98. <u--input v-model="inputForm.recordList[index].goodsName" placeholder="请选择物品名称"
  99. :disabled="nodeFlag" clearable></u--input>
  100. </u-form-item>
  101. <u-form-item label="分配生产日期" :prop="'recordList[' + index + '].fpDate'" :required="true">
  102. <u--input v-model="inputForm.recordList[index].fpDate" placeholder="请选择分配生产日期"
  103. :disabled="nodeFlag" clearable></u--input>
  104. </u-form-item>
  105. <u-form-item label="领用数量" :prop="'recordList[' + index + '].collectNumber'" :required="true">
  106. <u--input v-model="inputForm.recordList[index].collectNumber" placeholder="请输入领用数量"
  107. :disabled="nodeFlag" @blur="handleCollectNumberBlur(index)"></u--input>
  108. </u-form-item>
  109. <u-form-item label="库存数量" :prop="'recordList[' + index + '].kc'">
  110. <u--input v-model="inputForm.recordList[index].kc" disabled></u--input>
  111. </u-form-item>
  112. </view>
  113. </view>
  114. </u--form>
  115. <user-select ref="userPicker" @input="handleUserSelected"></user-select>
  116. <ba-tree-picker ref="treePicker" :multiple="false" @select-change="selectTypeChange" title="类型选择"
  117. :localdata="listData" :selectedData="typeSelectedData" valueKey="value" textKey="label"
  118. childrenKey="children" />
  119. </view>
  120. </template>
  121. <script>
  122. import {
  123. mapState
  124. } from 'vuex'
  125. import userSelect from '@/components/user-select/user-select-radio.vue'
  126. import baTreePicker from '@/components/ba-tree-picker/ba-tree-picker.vue'
  127. import UploadComponent from '@/pages/common/UploadComponent.vue'
  128. import upload from '@/utils/upload.js'
  129. import MaterialTypeService from '@/api/psi/MaterialTypeService'
  130. import WareHouseService from '@/api/psi/WareHouseService'
  131. import CollectService from '@/api/psi/CollectService'
  132. import CommonApi from '@/api/common/CommonApi'
  133. import CollectGoodsSelector from './CollectGoodsSelector.vue'
  134. export default {
  135. name: 'PsiCollectForm',
  136. components: {
  137. userSelect,
  138. baTreePicker,
  139. UploadComponent,
  140. CollectGoodsSelector
  141. },
  142. computed: mapState({
  143. userInfo: (state) => state.user.userInfo
  144. }),
  145. props: {
  146. businessId: {
  147. type: String,
  148. default: ''
  149. },
  150. formReadOnly: {
  151. type: Boolean,
  152. default: false
  153. },
  154. status: {
  155. type: String,
  156. default: ''
  157. }
  158. },
  159. data() {
  160. return {
  161. uploadUrl: upload.UPLOAD_URL,
  162. nodeFlag: false,
  163. loading: false,
  164. listData: [],
  165. materialList: [],
  166. typeSelectedData: [],
  167. inputForm: this.createInputForm(),
  168. rules: {
  169. collectDate: [{
  170. required: true,
  171. message: '领用时间不能为空',
  172. trigger: ['change']
  173. }]
  174. }
  175. }
  176. },
  177. materialTypeService: null,
  178. wareHouseService: null,
  179. collectService: null,
  180. commonApi: null,
  181. created() {
  182. this.ensureServices()
  183. this.loadMaterialTypes()
  184. this.fillUserInfo()
  185. },
  186. watch: {
  187. businessId: {
  188. handler() {
  189. if (this.businessId) {
  190. this.init(this.businessId)
  191. } else {
  192. this.resetForm()
  193. this.nodeFlag = this.formReadOnly || this.status === 'taskFormDetail' || this.status === 'testSee'
  194. }
  195. },
  196. immediate: true,
  197. deep: false
  198. }
  199. },
  200. methods: {
  201. createInputForm() {
  202. return {
  203. id: '',
  204. procInsId: '',
  205. processDefinitionId: '',
  206. status: '',
  207. collectNo: '',
  208. collectDate: new Date(),
  209. remarks: '',
  210. handledBy: '',
  211. handledById: '',
  212. handledByOffice: '',
  213. handledByOfficeName: '',
  214. userId: '',
  215. files: [],
  216. detailInfos: [],
  217. recordList: [],
  218. returnId: '',
  219. statusReturn: '',
  220. procInsIdReturn: '',
  221. processDefinitionIdReturn: '',
  222. returnCause: '',
  223. returnFiles: []
  224. }
  225. },
  226. createDetailRow() {
  227. return {
  228. id: '',
  229. recipientAgent: (this.userInfo && this.userInfo.name) || '',
  230. recipientAgentId: (this.userInfo && this.userInfo.id) || '',
  231. recipientOffice: (this.userInfo && this.userInfo.officeDTO && this.userInfo.officeDTO.name) || '',
  232. collectType: '',
  233. collectTypeId: '',
  234. goodsName: '',
  235. surplusNumber: '',
  236. currentInventory: '',
  237. collectNumber: '',
  238. company: '',
  239. brand: '',
  240. specification: '',
  241. remarks: '',
  242. spec: '1',
  243. produceDate: '',
  244. shelfLife: '',
  245. shelfLifeUnit: '',
  246. fileInfoLost: [],
  247. isReturn: '0'
  248. }
  249. },
  250. ensureServices() {
  251. if (!this.materialTypeService) {
  252. this.materialTypeService = new MaterialTypeService()
  253. }
  254. if (!this.wareHouseService) {
  255. this.wareHouseService = new WareHouseService()
  256. }
  257. if (!this.collectService) {
  258. this.collectService = new CollectService()
  259. }
  260. if (!this.commonApi) {
  261. this.commonApi = new CommonApi()
  262. }
  263. },
  264. async loadMaterialTypes() {
  265. try {
  266. const data = await this.materialTypeService.cgList()
  267. this.materialList = data || []
  268. this.listData = this.buildTree(this.materialList)
  269. } catch (e) {
  270. this.materialList = []
  271. this.listData = []
  272. }
  273. },
  274. fillUserInfo() {
  275. if (!this.userInfo) {
  276. return
  277. }
  278. this.inputForm.handledBy = this.inputForm.handledBy || this.userInfo.name
  279. this.inputForm.handledById = this.inputForm.handledById || this.userInfo.id
  280. this.inputForm.userId = this.inputForm.userId || this.userInfo.id
  281. this.inputForm.handledByOffice = this.inputForm.handledByOffice || (this.userInfo.officeDTO && this
  282. .userInfo.officeDTO.id) || ''
  283. this.inputForm.handledByOfficeName = this.inputForm.handledByOfficeName || (this.userInfo.officeDTO && this
  284. .userInfo.officeDTO.name) || ''
  285. },
  286. resetForm() {
  287. this.inputForm = this.createInputForm()
  288. this.fillUserInfo()
  289. },
  290. async init(id) {
  291. this.ensureServices()
  292. this.resetForm()
  293. this.inputForm.id = id
  294. this.loading = true
  295. try {
  296. const data = await this.collectService.findById(id)
  297. this.inputForm = this.normalizeFormData(this.recover(this.createInputForm(), data))
  298. this.inputForm.detailInfos.forEach(detail => {
  299. if (detail.surplusNumber && detail.spec) {
  300. detail.kc = this.formatNumber(Number(detail.surplusNumber) * Number(detail.spec))
  301. }
  302. })
  303. this.inputForm.recordList.forEach(record => {
  304. if (record.surplusNumber && record.spec) {
  305. record.kc = this.formatNumber(Number(record.surplusNumber) * Number(record.spec))
  306. }
  307. record.fpDate = `${record.goodsName}(生产日期:${record.produceDate ?? '--'})`
  308. })
  309. this.fillUserInfo()
  310. this.nodeFlag = await this.resolveNodeFlag(data)
  311. } catch (e) {
  312. this.nodeFlag = true
  313. } finally {
  314. this.loading = false
  315. }
  316. },
  317. async resolveNodeFlag(data) {
  318. if ((this.formReadOnly || this.status === 'taskFormDetail' || this.status === 'testSee')) {
  319. return true
  320. }
  321. try {
  322. const taskName = await this.commonApi.getTaskNameByProcInsId((data && data.procInsId) || this
  323. .inputForm.procInsId)
  324. if (this.isNotEmpty(taskName)) {
  325. return taskName !== '发起人重新发起申请'
  326. }
  327. } catch (e) {}
  328. return false
  329. },
  330. normalizeFormData(data) {
  331. const form = data || this.createInputForm()
  332. if (this.isNotEmpty(form.collectDate)) {
  333. form.collectDate = new Date(form.collectDate)
  334. }
  335. form.files = form.files || []
  336. form.detailInfos = (form.detailInfos || []).map(item => ({
  337. ...item,
  338. fileInfoLost: item.fileInfoLost || [],
  339. produceDate: this.isNotEmpty(item.produceDate) ? new Date(item.produceDate) : '',
  340. surplusNumber: this.isNotEmpty(item.surplusNumber) ? this.formatNumber(item
  341. .surplusNumber) : '',
  342. currentInventory: this.isNotEmpty(item.currentInventory) ? this.formatNumber(item
  343. .currentInventory) : ''
  344. }))
  345. form.recordList = form.recordList || []
  346. return form
  347. },
  348. buildTree(nodes, parentId = '0') {
  349. const tree = []
  350. for (const node of nodes || []) {
  351. if (String(node.infoType || node.info_type || '') === '1') {
  352. continue
  353. }
  354. if (node.parentId === parentId) {
  355. const children = this.buildTree(nodes, node.id)
  356. if (children.length) {
  357. node.children = children
  358. }
  359. tree.push(node)
  360. }
  361. }
  362. return tree
  363. },
  364. isEmpty(value) {
  365. if (value === null || value === undefined) {
  366. return true
  367. }
  368. if (typeof value === 'string' && value.trim() === '') {
  369. return true
  370. }
  371. if (Array.isArray(value) && value.length === 0) {
  372. return true
  373. }
  374. return false
  375. },
  376. isNotEmpty(value) {
  377. return !this.isEmpty(value)
  378. },
  379. formatDate(date) {
  380. if (this.isEmpty(date)) {
  381. return ''
  382. }
  383. const dateValue = new Date(date)
  384. if (Number.isNaN(dateValue.getTime())) {
  385. return ''
  386. }
  387. const year = dateValue.getFullYear()
  388. const month = `${dateValue.getMonth() + 1}`.padStart(2, '0')
  389. const day = `${dateValue.getDate()}`.padStart(2, '0')
  390. return `${year}-${month}-${day}`
  391. },
  392. normalizeDateFieldsBeforeValidate() {
  393. if (this.isNotEmpty(this.inputForm.collectDate)) {
  394. this.inputForm.collectDate = this.formatDate(this.inputForm.collectDate)
  395. }
  396. },
  397. formatNumber(value) {
  398. const numberValue = Number(value || 0)
  399. if (Number.isNaN(numberValue)) {
  400. return ''
  401. }
  402. return numberValue % 1 === 0 ? String(numberValue) : numberValue.toFixed(2).replace(/\.?0+$/, '')
  403. },
  404. formatNumberInput(inputValue, decimalLimit = 2) {
  405. if (this.isEmpty(inputValue)) {
  406. return ''
  407. }
  408. const valueText = String(inputValue)
  409. if (!/^\d*\.?\d*$/.test(valueText)) {
  410. return ''
  411. }
  412. let value = valueText.replace(/[^\d.]/g, '')
  413. const dotIndex = value.indexOf('.')
  414. if (dotIndex !== -1) {
  415. const substr = value.substr(dotIndex + 1)
  416. if (substr.indexOf('.') !== -1) {
  417. value = value.substr(0, dotIndex + 1) + substr.replace(/\./g, '')
  418. }
  419. }
  420. if (dotIndex !== -1) {
  421. const integerPart = value.substring(0, dotIndex)
  422. const decimalPart = value.substring(dotIndex + 1, dotIndex + 1 + decimalLimit)
  423. value = integerPart + '.' + decimalPart
  424. }
  425. return value
  426. },
  427. addDetail() {
  428. this.inputForm.detailInfos.push(this.createDetailRow())
  429. },
  430. removeDetail(index) {
  431. this.inputForm.detailInfos.splice(index, 1)
  432. },
  433. patchDetail(index, patch) {
  434. const detail = (this.inputForm.detailInfos || [])[index] || {}
  435. this.$set(this.inputForm.detailInfos, index, {
  436. ...detail,
  437. ...patch
  438. })
  439. },
  440. openUserPullForm(index) {
  441. if (this.nodeFlag) {
  442. return
  443. }
  444. this.$refs.userPicker.open(index, 'detail')
  445. },
  446. handleUserSelected(data, index, type) {
  447. if (type !== 'detail') {
  448. return
  449. }
  450. this.patchDetail(index, {
  451. recipientAgentId: data.id,
  452. recipientAgent: data.label,
  453. recipientOffice: data.parentLabel
  454. })
  455. },
  456. openTypePicker(index) {
  457. if (this.nodeFlag) {
  458. return
  459. }
  460. const detail = (this.inputForm.detailInfos || [])[index] || {}
  461. this.typeSelectedData = detail.collectTypeId ? [detail.collectTypeId] : []
  462. this.$nextTick(() => {
  463. const treePicker = this.$refs.treePicker
  464. if (!treePicker) {
  465. return
  466. }
  467. if (treePicker._initTree) {
  468. treePicker._initTree()
  469. }
  470. treePicker._show(index)
  471. })
  472. },
  473. selectTypeChange(ids, names, index) {
  474. this.patchDetail(index, {
  475. collectType: names,
  476. collectTypeId: ids[0],
  477. goodsName: '',
  478. surplusNumber: '',
  479. currentInventory: '',
  480. collectNumber: '',
  481. company: '',
  482. brand: '',
  483. specification: '',
  484. kc: '',
  485. spec: '1',
  486. produceDate: '',
  487. shelfLife: '',
  488. shelfLifeUnit: ''
  489. })
  490. },
  491. async handleGoodsSelected({
  492. index,
  493. item
  494. }) {
  495. const goodsName = item.tradeName || item.name || ''
  496. const stock = item.currentInventory || item.surplusNumber || ''
  497. this.patchDetail(index, {
  498. collectType: goodsName,
  499. collectTypeId: item.id || '',
  500. goodsName,
  501. brand: item.brand || '',
  502. specification: item.specification || '',
  503. company: item.company || '',
  504. spec: '1',
  505. surplusNumber: this.formatNumber(stock || 0),
  506. currentInventory: this.formatNumber(stock || 0),
  507. kc: this.formatNumber(stock || 0)
  508. })
  509. },
  510. async syncDetailStock(index) {
  511. const detail = (this.inputForm.detailInfos || [])[index]
  512. if (!detail || this.isEmpty(detail.goodsName) || this.isEmpty(detail.collectTypeId)) {
  513. return
  514. }
  515. try {
  516. const data = await this.wareHouseService.getByProduceDateNotMerge({
  517. current: 1,
  518. size: 1000,
  519. tradeName: detail.goodsName,
  520. wareHouseType: detail.collectTypeId
  521. })
  522. const records = (data && data.records) || []
  523. const totalBottle = records.reduce((sum, item) => {
  524. const currentInventory = Number(item.currentInventory || 0)
  525. const spec = Number(item.spec || 1)
  526. return sum + currentInventory * (spec > 0 ? spec : 1)
  527. }, 0)
  528. const firstRecord = records[0] || {}
  529. this.patchDetail(index, {
  530. surplusNumber: this.formatNumber(totalBottle),
  531. currentInventory: this.formatNumber(totalBottle),
  532. company: firstRecord.company || detail.company || '',
  533. brand: firstRecord.brand || detail.brand || '',
  534. specification: firstRecord.specification || detail.specification || '',
  535. spec: '1',
  536. goodsName : firstRecord.name,
  537. produceDate: this.isNotEmpty(firstRecord.produceDate) ? new Date(firstRecord
  538. .produceDate) : '',
  539. shelfLife: firstRecord.shelfLife || '',
  540. shelfLifeUnit: firstRecord.shelfLifeUnit || ''
  541. })
  542. } catch (e) {
  543. this.patchDetail(index, {
  544. surplusNumber: '',
  545. currentInventory: ''
  546. })
  547. uni.showToast({
  548. title: '库存数量加载失败',
  549. icon: 'none'
  550. })
  551. }
  552. },
  553. handleCollectNumberBlur(index) {
  554. const detail = (this.inputForm.detailInfos || [])[index]
  555. if (!detail) {
  556. return
  557. }
  558. detail.collectNumber = this.formatNumberInput(detail.collectNumber)
  559. if (this.isNotEmpty(detail.surplusNumber) && this.isNotEmpty(detail.collectNumber) &&
  560. Number(detail.collectNumber) > Number(detail.surplusNumber)) {
  561. uni.showToast({
  562. title: '领用数量不能大于库存数量',
  563. icon: 'none'
  564. })
  565. detail.collectNumber = ''
  566. }
  567. },
  568. handleUploadSuccess(file, fileList, index, type) {
  569. if (type === 'detail') {
  570. this.inputForm.detailInfos[index].fileInfoLost = fileList
  571. } else {
  572. this.inputForm.files = fileList
  573. }
  574. },
  575. handleRemove(file, fileList, lineIndex, fileIndex, type) {
  576. if (type === 'detail') {
  577. this.inputForm.detailInfos[lineIndex].fileInfoLost.splice(fileIndex, 1)
  578. } else {
  579. this.inputForm.files.splice(fileIndex, 1)
  580. }
  581. },
  582. validateDetailInfos() {
  583. if (this.isEmpty(this.inputForm.detailInfos)) {
  584. this.$message.error('至少填写一条领用详情信息')
  585. return false
  586. }
  587. for (let i = 0; i < this.inputForm.detailInfos.length; i++) {
  588. const detail = this.inputForm.detailInfos[i]
  589. const lineNo = i + 1
  590. // if (this.isEmpty(detail.recipientAgentId)) {
  591. // uni.showToast({
  592. // title: `领用详情第${lineNo}行请选择领用人`,
  593. // icon: 'none'
  594. // })
  595. // return false
  596. // }
  597. if (this.isEmpty(detail.collectTypeId)) {
  598. uni.showToast({
  599. title: `领用详情第${lineNo}行请选择物品名称`,
  600. icon: 'none'
  601. })
  602. return false
  603. }
  604. // if (this.isEmpty(detail.goodsName)) {
  605. // uni.showToast({
  606. // title: `领用详情第${lineNo}行请选择物品名称`,
  607. // icon: 'none'
  608. // })
  609. // return false
  610. // }
  611. if (this.isEmpty(detail.collectNumber)) {
  612. uni.showToast({
  613. title: `领用详情第${lineNo}行请输入领用数量`,
  614. icon: 'none'
  615. })
  616. return false
  617. }
  618. if (this.isNotEmpty(detail.surplusNumber) && Number(detail.collectNumber) > Number(detail
  619. .surplusNumber)) {
  620. uni.showToast({
  621. title: `领用详情第${lineNo}行领用数量不能大于库存数量`,
  622. icon: 'none'
  623. })
  624. return false
  625. }
  626. }
  627. return true
  628. },
  629. toSubmitData() {
  630. const data = JSON.parse(JSON.stringify(this.inputForm))
  631. data.collectDate = this.formatDate(this.inputForm.collectDate)
  632. data.detailInfos = (this.inputForm.detailInfos || []).map(item => ({
  633. ...JSON.parse(JSON.stringify(item)),
  634. produceDate: this.formatDate(item.produceDate)
  635. }))
  636. return data
  637. },
  638. async saveForm(callback) {
  639. return new Promise((resolve, reject) => {
  640. // 表单规则验证
  641. // ...
  642. let errors = [];
  643. if (this.isNotEmpty(this.inputForm.collectDate)) {
  644. this.inputForm.collectDate = this.formatDate(this.inputForm.collectDate);
  645. }
  646. if (this.isEmpty(this.inputForm.detailInfos)) {
  647. errors.push('至少填写一条领用详情信息');
  648. } else {
  649. let i = this.inputForm.detailInfos.length;
  650. for (let j = 0; j < i; j++) {
  651. let k = j + 1;
  652. if (this.isEmpty(this.inputForm.detailInfos[j].collectType)) {
  653. errors.push('领用详情第' + k + '行请选择物品名称');
  654. } else if (this.isEmpty(this.inputForm.detailInfos[j].collectNumber)) {
  655. errors.push('领用详情第' + k + '行请输入领用数量');
  656. }
  657. if (parseFloat(this.inputForm.detailInfos[j].collectNumber) > parseFloat(this
  658. .inputForm.detailInfos[j].surplusNumber) * parseFloat(this.inputForm
  659. .detailInfos[j].spec)) {
  660. errors.push('领用详情第' + k + '行请输入领用数量');
  661. }
  662. }
  663. }
  664. if (errors.length > 0) {
  665. // 存在错误,显示提示信息
  666. errors.forEach(error => {
  667. uni.showToast({
  668. title: error,
  669. icon: 'none',
  670. duration: 2000
  671. });
  672. });
  673. reject('Form validation failed');
  674. } else {
  675. // 所有验证通过,执行保存操作
  676. this.$refs.inputForm.validate().then(async () => {
  677. uni.showLoading();
  678. try {
  679. const submitData = this.toSubmitData()
  680. submitData.status = '2'
  681. this.inputForm.status = '2'
  682. const data = await this.collectService.save(submitData)
  683. callback(data.businessTable, data.businessId, submitData)
  684. resolve('Form saved successfully');
  685. } finally {
  686. reject('Save operation failed');
  687. }
  688. }).catch(() => {
  689. reject('Form validation failed');
  690. });
  691. }
  692. });
  693. },
  694. async startForm(callback) {
  695. this.loading = true
  696. try {
  697. if (this.isNotEmpty(this.inputForm.id)) {
  698. const data = await this.collectService.findById(this.inputForm.id)
  699. if (this.isNotEmpty(data.status) && !['0', '1', '3'].includes(data.status)) {
  700. uni.showToast({
  701. title: '任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在',
  702. icon: 'none'
  703. })
  704. throw new Error('invalid status')
  705. }
  706. }
  707. await this.startFormTrue(callback)
  708. } finally {
  709. this.loading = false
  710. }
  711. },
  712. async startFormTrue(callback) {
  713. this.normalizeDateFieldsBeforeValidate()
  714. await this.$refs.inputForm.validate()
  715. if (!this.validateDetailInfos()) {
  716. return
  717. }
  718. const submitData = this.toSubmitData()
  719. submitData.status = '2'
  720. this.inputForm.status = '2'
  721. const data = await this.collectService.save(submitData)
  722. this.inputForm.id = data.businessId
  723. callback(data.businessTable, data.businessId, submitData)
  724. },
  725. async reapplyForm(callback) {
  726. this.loading = true
  727. try {
  728. const data = await this.collectService.findById(this.inputForm.id)
  729. if (data.status !== '4') {
  730. uni.showToast({
  731. title: '任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在',
  732. icon: 'none'
  733. })
  734. throw new Error('invalid status')
  735. }
  736. await this.startFormTrue(callback)
  737. } finally {
  738. this.loading = false
  739. }
  740. },
  741. async agreeForm(callback) {
  742. this.loading = true
  743. try {
  744. this.normalizeDateFieldsBeforeValidate()
  745. await this.$refs.inputForm.validate()
  746. if (!this.validateDetailInfos()) {
  747. return
  748. }
  749. const submitData = this.toSubmitData()
  750. try {
  751. const taskName = await this.commonApi.getTaskNameByProcInsId(this.inputForm.procInsId)
  752. if (this.isNotEmpty(taskName) && taskName === '老板审核') {
  753. submitData.status = '5'
  754. this.inputForm.status = '5'
  755. }
  756. } catch (e) {}
  757. const data = await this.collectService.save(submitData)
  758. callback(data.businessTable, data.businessId, submitData)
  759. } finally {
  760. this.loading = false
  761. }
  762. },
  763. async updateStatusById(type, callback) {
  764. this.loading = true
  765. try {
  766. if (type === 'reject' || type === 'reback') {
  767. const data = await this.collectService.findById(this.inputForm.id)
  768. if (data.status !== '2') {
  769. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  770. throw new Error('invalid status')
  771. }
  772. const nextStatus = type === 'reject' ? '4' : '3'
  773. this.inputForm.status = nextStatus
  774. await this.collectService.updateStatusById({
  775. status: nextStatus,
  776. id: this.inputForm.id
  777. })
  778. callback()
  779. } else if (type === 'hold') {
  780. const data = await this.collectService.findById(this.inputForm.id)
  781. if (data.status !== '4') {
  782. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  783. throw new Error('invalid status')
  784. }
  785. this.inputForm.status = '1'
  786. await this.collectService.updateStatusById({
  787. status: '1',
  788. id: this.inputForm.id
  789. })
  790. callback()
  791. }
  792. } finally {
  793. this.loading = false
  794. }
  795. },
  796. async openPicker(index) {
  797. await this.$refs.goodsSelect.loadGoodsList()
  798. },
  799. }
  800. }
  801. </script>
  802. <style scoped>
  803. .section-wrap {
  804. margin-top: 24rpx;
  805. }
  806. .section-title {
  807. margin-bottom: 8rpx;
  808. padding-left: 8rpx;
  809. font-size: 30rpx;
  810. font-weight: 700;
  811. color: #1f2937;
  812. border-left: 6rpx solid #2979ff;
  813. }
  814. .section-tip {
  815. margin-bottom: 16rpx;
  816. font-size: 24rpx;
  817. color: #64748b;
  818. }
  819. .detail-card {
  820. margin-bottom: 20rpx;
  821. padding: 24rpx;
  822. background: #fff;
  823. border-radius: 20rpx;
  824. box-shadow: 0 10rpx 30rpx rgba(15, 23, 42, 0.05);
  825. }
  826. .detail-card-title {
  827. margin-bottom: 12rpx;
  828. font-size: 28rpx;
  829. font-weight: 600;
  830. color: #0f172a;
  831. }
  832. </style>