LossForm.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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="lossNo">
  9. <u--input placeholder="自动生成" v-model="inputForm.lossNo" 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="lossDate" :required="true">
  18. <el-date-picker v-model="inputForm.lossDate" 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. <u-form-item label="报损类型" :prop="'detailInfos[' + index + '].collectType'" :required="true">
  39. <u--input v-model="inputForm.detailInfos[index].collectType" placeholder="请选择报损类型"
  40. :disabled="nodeFlag" @focus="openTypePicker(index)" clearable></u--input>
  41. </u-form-item>
  42. <loss-goods-selector :rowIndex="index" :inputForm="inputForm" :disabled="nodeFlag"
  43. @selected="handleGoodsSelected"></loss-goods-selector>
  44. <u-form-item label="包装规格" :prop="'detailInfos[' + index + '].spec'">
  45. <u--input v-model="inputForm.detailInfos[index].spec" disabled></u--input>
  46. </u-form-item>
  47. <u-form-item label="生产日期" :prop="'detailInfos[' + index + '].produceDate'">
  48. <u--input :value="formatDisplayDate(inputForm.detailInfos[index].produceDate)" disabled>
  49. </u--input>
  50. </u-form-item>
  51. <!-- <u-form-item label="库存数量" :prop="'detailInfos[' + index + '].surplusNumber'">
  52. <u--input v-model="inputForm.detailInfos[index].surplusNumber" disabled></u--input>
  53. </u-form-item> -->
  54. <!-- <u-form-item label="单位" :prop="'detailInfos[' + index + '].company'">
  55. <u--input v-model="inputForm.detailInfos[index].company" disabled></u--input>
  56. </u-form-item> -->
  57. <u-form-item label="报损数量" :prop="'detailInfos[' + index + '].lossNumber'" :required="true">
  58. <u--input v-model="inputForm.detailInfos[index].lossNumber" placeholder="请输入报损数量"
  59. :disabled="nodeFlag" @blur="handleLossNumberBlur(index)"></u--input>
  60. </u-form-item>
  61. <u-form-item label="附件上传">
  62. <UploadComponent :uploadUrl="`${uploadUrl}/public-modules-server/oss/file/webUpload/upload`"
  63. @onRemove="(file, fileList, fileIndex) => handleRemove(file, fileList, index, fileIndex, 'detail')"
  64. @onSuccess="(file, fileList) => handleUploadSuccess(file, fileList, index, 'detail')"
  65. :fileList="inputForm.detailInfos[index].fileInfoLost" :limit="3" :isDelete="nodeFlag"
  66. :isUpload="nodeFlag">
  67. </UploadComponent>
  68. </u-form-item>
  69. <u-form-item label="" v-if="!nodeFlag">
  70. <el-button style="width: 100%" type="danger" plain @click="removeDetail(index)">
  71. 删除报损详情
  72. </el-button>
  73. </u-form-item>
  74. </view>
  75. <u-form-item label="" v-if="!nodeFlag">
  76. <el-button style="width: 100%" type="primary" @click="addDetail()" plain>新增报损详情</el-button>
  77. </u-form-item>
  78. </view>
  79. <u-form-item label="附件">
  80. <UploadComponent :uploadUrl="`${uploadUrl}/public-modules-server/oss/file/webUpload/upload`"
  81. @onRemove="(file, fileList, fileIndex) => handleRemove(file, fileList, '', fileIndex, '')"
  82. @onSuccess="(file, fileList) => handleUploadSuccess(file, fileList, '', '')"
  83. :fileList="inputForm.files" :limit="3" :isDelete="nodeFlag" :isUpload="nodeFlag">
  84. </UploadComponent>
  85. </u-form-item>
  86. </u--form>
  87. <user-select ref="userPicker" @input="handleUserSelected"></user-select>
  88. <ba-tree-picker ref="treePicker" :multiple="false" @select-change="selectTypeChange" title="类型选择"
  89. :localdata="listData" valueKey="value" textKey="label" childrenKey="children" />
  90. </view>
  91. </template>
  92. <script>
  93. import { mapState } from 'vuex'
  94. import userSelect from '@/components/user-select/user-select-radio.vue'
  95. import baTreePicker from '@/components/ba-tree-picker/ba-tree-picker.vue'
  96. import UploadComponent from '@/pages/common/UploadComponent.vue'
  97. import upload from '@/utils/upload.js'
  98. import MaterialTypeService from '@/api/psi/MaterialTypeService'
  99. import WareHouseService from '@/api/psi/WareHouseService'
  100. import LossService from '@/api/psi/LossService'
  101. import CommonApi from '@/api/common/CommonApi'
  102. import LossGoodsSelector from './LossGoodsSelector.vue'
  103. export default {
  104. name: 'PsiLossForm',
  105. components: {
  106. userSelect,
  107. baTreePicker,
  108. UploadComponent,
  109. LossGoodsSelector
  110. },
  111. computed: mapState({
  112. userInfo: (state) => state.user.userInfo
  113. }),
  114. props: {
  115. businessId: {
  116. type: String,
  117. default: ''
  118. },
  119. formReadOnly: {
  120. type: Boolean,
  121. default: false
  122. },
  123. status: {
  124. type: String,
  125. default: ''
  126. },
  127. isCc: {
  128. type: Boolean,
  129. default: false
  130. }
  131. },
  132. data() {
  133. return {
  134. uploadUrl: upload.UPLOAD_URL,
  135. nodeFlag: false,
  136. loading: false,
  137. listData: [],
  138. materialList: [],
  139. inputForm: this.createInputForm(),
  140. rules: {
  141. lossDate: [
  142. {
  143. required: true,
  144. message: '报损时间不能为空',
  145. trigger: ['change']
  146. }
  147. ]
  148. }
  149. }
  150. },
  151. materialTypeService: null,
  152. wareHouseService: null,
  153. lossService: null,
  154. commonApi: null,
  155. created() {
  156. this.ensureServices()
  157. this.loadMaterialTypes()
  158. this.fillUserInfo()
  159. },
  160. watch: {
  161. businessId: {
  162. handler() {
  163. if (this.businessId) {
  164. this.init(this.businessId)
  165. } else {
  166. this.resetForm()
  167. this.nodeFlag = this.formReadOnly || this.status === 'taskFormDetail' || this.status === 'testSee'
  168. }
  169. },
  170. immediate: true,
  171. deep: false
  172. }
  173. },
  174. methods: {
  175. createInputForm() {
  176. return {
  177. id: '',
  178. procInsId: '',
  179. processDefinitionId: '',
  180. status: '',
  181. lossNo: '',
  182. lossDate: '',
  183. remarks: '',
  184. handledBy: '',
  185. handledById: '',
  186. handledByOffice: '',
  187. handledByOfficeName: '',
  188. userId: '',
  189. files: [],
  190. detailInfos: []
  191. }
  192. },
  193. createDetailRow() {
  194. return {
  195. id: '',
  196. recipientAgent: (this.userInfo && this.userInfo.name) || '',
  197. recipientAgentId: (this.userInfo && this.userInfo.id) || '',
  198. recipientOffice: (this.userInfo && this.userInfo.officeDTO && this.userInfo.officeDTO.name) || '',
  199. collectType: '',
  200. collectTypeId: '',
  201. goodsName: '',
  202. lossNumber: '',
  203. company: '',
  204. produceDate: '',
  205. shelfLife: '',
  206. shelfLifeUnit: '',
  207. spec: '',
  208. surplusNumber: '',
  209. currentInventory: '',
  210. fileInfoLost: []
  211. }
  212. },
  213. ensureServices() {
  214. if (!this.materialTypeService) {
  215. this.materialTypeService = new MaterialTypeService()
  216. }
  217. if (!this.wareHouseService) {
  218. this.wareHouseService = new WareHouseService()
  219. }
  220. if (!this.lossService) {
  221. this.lossService = new LossService()
  222. }
  223. if (!this.commonApi) {
  224. this.commonApi = new CommonApi()
  225. }
  226. },
  227. async loadMaterialTypes() {
  228. try {
  229. const data = await this.materialTypeService.cgList()
  230. this.materialList = data || []
  231. this.listData = this.buildTree(this.materialList)
  232. } catch (e) {
  233. this.materialList = []
  234. this.listData = []
  235. }
  236. },
  237. fillUserInfo() {
  238. if (!this.userInfo) {
  239. return
  240. }
  241. this.inputForm.handledBy = this.inputForm.handledBy || this.userInfo.name
  242. this.inputForm.handledById = this.inputForm.handledById || this.userInfo.id
  243. this.inputForm.userId = this.inputForm.userId || this.userInfo.id
  244. this.inputForm.handledByOffice = this.inputForm.handledByOffice || (this.userInfo.officeDTO && this.userInfo.officeDTO.id) || ''
  245. this.inputForm.handledByOfficeName = this.inputForm.handledByOfficeName || (this.userInfo.officeDTO && this.userInfo.officeDTO.name) || ''
  246. },
  247. resetForm() {
  248. this.inputForm = this.createInputForm()
  249. this.fillUserInfo()
  250. },
  251. async init(id) {
  252. this.ensureServices()
  253. this.resetForm()
  254. this.inputForm.id = id
  255. this.loading = true
  256. try {
  257. const data = await this.lossService.findById(id)
  258. this.inputForm = this.normalizeFormData(this.recover(this.createInputForm(), data))
  259. this.fillUserInfo()
  260. this.nodeFlag = await this.resolveNodeFlag(data)
  261. } catch (e) {
  262. this.nodeFlag = true
  263. } finally {
  264. this.loading = false
  265. }
  266. },
  267. async resolveNodeFlag(data) {
  268. if (this.formReadOnly || this.status === 'taskFormDetail' || this.status === 'testSee') {
  269. return true
  270. }
  271. try {
  272. const taskName = await this.commonApi.getTaskNameByProcInsId((data && data.procInsId) || this.inputForm.procInsId)
  273. if (this.isNotEmpty(taskName)) {
  274. return taskName !== '发起人重新发起申请'
  275. }
  276. } catch (e) {
  277. }
  278. return false
  279. },
  280. normalizeFormData(data) {
  281. const form = data || this.createInputForm()
  282. if (this.isNotEmpty(form.lossDate)) {
  283. form.lossDate = new Date(form.lossDate)
  284. }
  285. form.files = form.files || []
  286. form.detailInfos = (form.detailInfos || []).map(item => ({
  287. ...item,
  288. fileInfoLost: item.fileInfoLost || [],
  289. produceDate: this.isNotEmpty(item.produceDate) ? new Date(item.produceDate) : '',
  290. surplusNumber: this.isNotEmpty(item.surplusNumber) ? this.formatNumber(item.surplusNumber) : ''
  291. }))
  292. return form
  293. },
  294. buildTree(nodes, parentId = '0') {
  295. const tree = []
  296. for (const node of nodes || []) {
  297. if (node.parentId === parentId) {
  298. const children = this.buildTree(nodes, node.id)
  299. if (children.length) {
  300. node.children = children
  301. }
  302. tree.push(node)
  303. }
  304. }
  305. return tree
  306. },
  307. isEmpty(value) {
  308. if (value === null || value === undefined) {
  309. return true
  310. }
  311. if (typeof value === 'string' && value.trim() === '') {
  312. return true
  313. }
  314. if (Array.isArray(value) && value.length === 0) {
  315. return true
  316. }
  317. return false
  318. },
  319. isNotEmpty(value) {
  320. return !this.isEmpty(value)
  321. },
  322. formatDate(date) {
  323. if (this.isEmpty(date)) {
  324. return ''
  325. }
  326. const dateValue = new Date(date)
  327. if (Number.isNaN(dateValue.getTime())) {
  328. return ''
  329. }
  330. const year = dateValue.getFullYear()
  331. const month = `${dateValue.getMonth() + 1}`.padStart(2, '0')
  332. const day = `${dateValue.getDate()}`.padStart(2, '0')
  333. return `${year}-${month}-${day}`
  334. },
  335. formatDisplayDate(date) {
  336. return this.formatDate(date)
  337. },
  338. normalizeDateFieldsBeforeValidate() {
  339. if (this.isNotEmpty(this.inputForm.lossDate)) {
  340. this.inputForm.lossDate = this.formatDate(this.inputForm.lossDate)
  341. }
  342. },
  343. formatNumber(value) {
  344. const numberValue = Number(value || 0)
  345. if (Number.isNaN(numberValue)) {
  346. return ''
  347. }
  348. return numberValue % 1 === 0 ? String(numberValue) : numberValue.toFixed(2).replace(/\.?0+$/, '')
  349. },
  350. formatNumberInput(inputValue, decimalLimit = 2) {
  351. if (this.isEmpty(inputValue)) {
  352. return ''
  353. }
  354. const valueText = String(inputValue)
  355. if (!/^\d*\.?\d*$/.test(valueText)) {
  356. return ''
  357. }
  358. let value = valueText.replace(/[^\d.]/g, '')
  359. const dotIndex = value.indexOf('.')
  360. if (dotIndex !== -1) {
  361. const substr = value.substr(dotIndex + 1)
  362. if (substr.indexOf('.') !== -1) {
  363. value = value.substr(0, dotIndex + 1) + substr.replace(/\./g, '')
  364. }
  365. }
  366. if (dotIndex !== -1) {
  367. const integerPart = value.substring(0, dotIndex)
  368. const decimalPart = value.substring(dotIndex + 1, dotIndex + 1 + decimalLimit)
  369. value = integerPart + '.' + decimalPart
  370. }
  371. return value
  372. },
  373. addDetail() {
  374. if (this.inputForm.detailInfos.length > 0) {
  375. this.$message.warning('报损明细只能保留一条')
  376. return
  377. }
  378. this.inputForm.detailInfos.push(this.createDetailRow())
  379. },
  380. removeDetail(index) {
  381. this.inputForm.detailInfos.splice(index, 1)
  382. },
  383. patchDetail(index, patch) {
  384. const detail = (this.inputForm.detailInfos || [])[index] || {}
  385. this.$set(this.inputForm.detailInfos, index, {
  386. ...detail,
  387. ...patch
  388. })
  389. },
  390. openUserPullForm(index) {
  391. if (this.nodeFlag) {
  392. return
  393. }
  394. this.$refs.userPicker.open(index, 'detail')
  395. },
  396. handleUserSelected(data, index, type) {
  397. if (type !== 'detail') {
  398. return
  399. }
  400. this.patchDetail(index, {
  401. recipientAgentId: data.id,
  402. recipientAgent: data.label,
  403. recipientOffice: data.parentLabel
  404. })
  405. },
  406. openTypePicker(index) {
  407. if (this.nodeFlag) {
  408. return
  409. }
  410. this.$refs.treePicker._show(index)
  411. },
  412. selectTypeChange(ids, names, index) {
  413. this.patchDetail(index, {
  414. collectType: names,
  415. collectTypeId: ids[0],
  416. goodsName: '',
  417. company: '',
  418. produceDate: '',
  419. shelfLife: '',
  420. shelfLifeUnit: '',
  421. spec: '',
  422. surplusNumber: '',
  423. currentInventory: '',
  424. lossNumber: ''
  425. })
  426. },
  427. async handleGoodsSelected({ index, item }) {
  428. this.patchDetail(index, {
  429. goodsName: item.tradeName || '',
  430. company: item.company || '',
  431. produceDate: this.isNotEmpty(item.produceDate) ? new Date(item.produceDate) : '',
  432. shelfLife: item.shelfLife || '',
  433. shelfLifeUnit: item.shelfLifeUnit || '',
  434. spec: item.spec || '',
  435. currentInventory: item.currentInventory || ''
  436. })
  437. await this.syncDetailStock(index)
  438. },
  439. async syncDetailStock(index) {
  440. const detail = (this.inputForm.detailInfos || [])[index]
  441. if (!detail || this.isEmpty(detail.goodsName) || this.isEmpty(detail.collectTypeId)) {
  442. return
  443. }
  444. try {
  445. const data = await this.wareHouseService.getByProduceDateNotMerge({
  446. current: 1,
  447. size: 1000,
  448. tradeName: detail.goodsName,
  449. wareHouseType: detail.collectTypeId
  450. })
  451. const records = (data && data.records) || []
  452. const total = records.reduce((sum, item) => {
  453. const currentInventory = Number(item.currentInventory || 0)
  454. const spec = Number(item.spec || 1)
  455. return sum + currentInventory * (spec > 0 ? spec : 1)
  456. }, 0)
  457. this.$set(this.inputForm.detailInfos[index], 'surplusNumber', this.formatNumber(total))
  458. } catch (e) {
  459. this.$set(this.inputForm.detailInfos[index], 'surplusNumber', '')
  460. uni.showToast({
  461. title: '库存数量加载失败',
  462. icon: 'none'
  463. })
  464. }
  465. },
  466. handleUploadSuccess(file, fileList, index, type) {
  467. if (type === 'detail') {
  468. this.inputForm.detailInfos[index].fileInfoLost = fileList
  469. } else {
  470. this.inputForm.files = fileList
  471. }
  472. },
  473. handleRemove(file, fileList, lineIndex, fileIndex, type) {
  474. if (type === 'detail') {
  475. this.inputForm.detailInfos[lineIndex].fileInfoLost.splice(fileIndex, 1)
  476. } else {
  477. this.inputForm.files.splice(fileIndex, 1)
  478. }
  479. },
  480. handleLossNumberBlur(index) {
  481. const detail = (this.inputForm.detailInfos || [])[index]
  482. if (!detail) {
  483. return
  484. }
  485. detail.lossNumber = this.formatNumberInput(detail.lossNumber)
  486. if (this.isNotEmpty(detail.surplusNumber) && this.isNotEmpty(detail.lossNumber) &&
  487. Number(detail.lossNumber) > Number(detail.surplusNumber)) {
  488. this.$message.error('报损数量不能大于当前库存')
  489. detail.lossNumber = ''
  490. }
  491. },
  492. validateDetailInfos(checkStock = true) {
  493. if (this.isEmpty(this.inputForm.detailInfos)) {
  494. this.$message.error('请填写报损明细')
  495. return false
  496. }
  497. if (this.inputForm.detailInfos.length !== 1) {
  498. this.$message.error('报损明细只能保留一条')
  499. return false
  500. }
  501. const detail = this.inputForm.detailInfos[0]
  502. if (this.isEmpty(detail.recipientAgentId)) {
  503. this.$message.error('请选择报损人')
  504. return false
  505. }
  506. if (this.isEmpty(detail.collectTypeId)) {
  507. this.$message.error('请选择报损类型')
  508. return false
  509. }
  510. if (this.isEmpty(detail.goodsName)) {
  511. this.$message.error('请选择物品名称')
  512. return false
  513. }
  514. if (this.isEmpty(detail.lossNumber)) {
  515. this.$message.error('请输入报损数量')
  516. return false
  517. }
  518. if (checkStock && this.isNotEmpty(detail.surplusNumber) && Number(detail.lossNumber) > Number(detail.surplusNumber)) {
  519. this.$message.error('报损数量不能大于当前库存')
  520. return false
  521. }
  522. return true
  523. },
  524. toSubmitData() {
  525. const data = JSON.parse(JSON.stringify(this.inputForm))
  526. data.lossDate = this.formatDate(this.inputForm.lossDate)
  527. data.detailInfos = (this.inputForm.detailInfos || []).map(item => ({
  528. ...JSON.parse(JSON.stringify(item)),
  529. produceDate: this.formatDate(item.produceDate)
  530. }))
  531. return data
  532. },
  533. async saveForm(callback) {
  534. this.loading = true
  535. try {
  536. const submitData = this.toSubmitData()
  537. submitData.status = '1'
  538. this.inputForm.status = '1'
  539. const data = await this.lossService.save(submitData)
  540. callback(data.businessTable, data.businessId, submitData)
  541. } finally {
  542. this.loading = false
  543. }
  544. },
  545. async startForm(callback) {
  546. this.loading = true
  547. try {
  548. if (this.isNotEmpty(this.inputForm.id)) {
  549. const data = await this.lossService.findById(this.inputForm.id)
  550. if (this.isNotEmpty(data.status) && !['0', '1', '3'].includes(data.status)) {
  551. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  552. throw new Error('invalid status')
  553. }
  554. }
  555. await this.startFormTrue(callback)
  556. } finally {
  557. this.loading = false
  558. }
  559. },
  560. async startFormTrue(callback) {
  561. this.normalizeDateFieldsBeforeValidate()
  562. await this.$refs.inputForm.validate()
  563. if (!this.validateDetailInfos(true)) {
  564. return
  565. }
  566. const submitData = this.toSubmitData()
  567. submitData.status = '2'
  568. this.inputForm.status = '2'
  569. const data = await this.lossService.save(submitData)
  570. this.inputForm.id = data.businessId
  571. callback(data.businessTable, data.businessId, submitData)
  572. },
  573. async reapplyForm(callback) {
  574. this.loading = true
  575. try {
  576. const data = await this.lossService.findById(this.inputForm.id)
  577. if (data.status !== '4') {
  578. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  579. throw new Error('invalid status')
  580. }
  581. await this.startFormTrue(callback)
  582. } finally {
  583. this.loading = false
  584. }
  585. },
  586. async agreeForm(callback) {
  587. this.loading = true
  588. try {
  589. this.normalizeDateFieldsBeforeValidate()
  590. await this.$refs.inputForm.validate()
  591. if (!this.validateDetailInfos(false)) {
  592. return
  593. }
  594. const submitData = this.toSubmitData()
  595. submitData.status = '5'
  596. this.inputForm.status = '5'
  597. const data = await this.lossService.save(submitData)
  598. callback(data.businessTable, data.businessId, submitData)
  599. } finally {
  600. this.loading = false
  601. }
  602. },
  603. async updateStatusById(type, callback) {
  604. this.loading = true
  605. try {
  606. if (type === 'reject' || type === 'reback') {
  607. const data = await this.lossService.findById(this.inputForm.id)
  608. if (data.status !== '2') {
  609. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  610. throw new Error('invalid status')
  611. }
  612. const nextStatus = type === 'reject' ? '4' : '3'
  613. this.inputForm.status = nextStatus
  614. await this.lossService.updateStatusById({
  615. status: nextStatus,
  616. id: this.inputForm.id
  617. })
  618. callback()
  619. } else if (type === 'hold') {
  620. const data = await this.lossService.findById(this.inputForm.id)
  621. if (data.status !== '4') {
  622. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  623. throw new Error('invalid status')
  624. }
  625. this.inputForm.status = '1'
  626. await this.lossService.updateStatusById({
  627. status: '1',
  628. id: this.inputForm.id
  629. })
  630. callback()
  631. }
  632. } finally {
  633. this.loading = false
  634. }
  635. }
  636. }
  637. }
  638. </script>
  639. <style scoped>
  640. .section-wrap {
  641. margin-top: 24rpx;
  642. }
  643. .section-title {
  644. margin-bottom: 8rpx;
  645. padding-left: 8rpx;
  646. font-size: 30rpx;
  647. font-weight: 700;
  648. color: #1f2937;
  649. border-left: 6rpx solid #2979ff;
  650. }
  651. .section-tip {
  652. margin-bottom: 16rpx;
  653. font-size: 24rpx;
  654. color: #64748b;
  655. }
  656. .detail-card {
  657. margin-bottom: 20rpx;
  658. padding: 24rpx;
  659. background: #fff;
  660. border-radius: 20rpx;
  661. box-shadow: 0 10rpx 30rpx rgba(15, 23, 42, 0.05);
  662. }
  663. .detail-card-title {
  664. margin-bottom: 12rpx;
  665. font-size: 28rpx;
  666. font-weight: 600;
  667. color: #0f172a;
  668. }
  669. </style>