WareHouseUpdateForm.vue 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  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="wareHouseNumber">
  9. <u--input placeholder="自动生成" v-model="inputForm.wareHouseNumber" disabled></u--input>
  10. </u-form-item>
  11. <u-form-item label="入库名称" borderBottom prop="wareHouseName" :required="true">
  12. <u--input placeholder="请填写入库名称" v-model="inputForm.wareHouseName" :disabled="nodeFlag"></u--input>
  13. </u-form-item>
  14. <u-form-item label="经办人" borderBottom prop="handledBy">
  15. <u--input v-model="inputForm.handledBy" disabled></u--input>
  16. </u-form-item>
  17. <u-form-item label="经办部门" borderBottom prop="handledByOfficeName">
  18. <u--input v-model="inputForm.handledByOfficeName" disabled></u--input>
  19. </u-form-item>
  20. <u-form-item label="入库时间" borderBottom prop="wareHouseDate" :required="true">
  21. <el-date-picker v-model="inputForm.wareHouseDate" type="date" value-format="yyyy-MM-dd"
  22. placeholder="请选择入库时间" style="width:100%" placement="bottom-start" clearable :disabled="nodeFlag">
  23. </el-date-picker>
  24. </u-form-item>
  25. <purchase-selector :inputForm="inputForm" :disabled="nodeFlag" @selected="handlePurchaseSelected">
  26. </purchase-selector>
  27. <u-form-item label="备注" borderBottom prop="remarks">
  28. <u--textarea placeholder="请填写备注信息" :rows="4" :maxlength="500" v-model="inputForm.remarks"
  29. :disabled="nodeFlag"></u--textarea>
  30. </u-form-item>
  31. <view v-if="inputForm.purchaseNo" class="section-wrap">
  32. <view class="section-title">采购详情</view>
  33. <view v-for="(item, index) in inputForm.detailInfos" :key="item.id || index" class="detail-card">
  34. <view class="detail-card-title">采购明细 {{ index + 1 }}</view>
  35. <view class="detail-grid">
  36. <!-- <view class="detail-item">
  37. <text class="detail-label">采购人</text>
  38. <text class="detail-value">{{ item.purchaserAgent || '-' }}</text>
  39. </view>
  40. <view class="detail-item">
  41. <text class="detail-label">采购部门</text>
  42. <text class="detail-value">{{ item.procurementOffice || '-' }}</text>
  43. </view> -->
  44. <view class="detail-item">
  45. <text class="detail-label">商品名称</text>
  46. <text class="detail-value">{{ item.procurementType || '-' }}</text>
  47. </view>
  48. <!-- <view class="detail-item">
  49. <text class="detail-label">商品名称</text>
  50. <text class="detail-value">{{ item.tradeName || '-' }}</text>
  51. </view> -->
  52. <view class="detail-item">
  53. <text class="detail-label">品牌</text>
  54. <text class="detail-value">{{ item.brand || '-' }}</text>
  55. </view>
  56. <view class="detail-item">
  57. <text class="detail-label">规格</text>
  58. <text class="detail-value">{{ item.specification || '-' }}</text>
  59. </view>
  60. <view class="detail-item">
  61. <text class="detail-label">单位</text>
  62. <text class="detail-value">{{ item.company || '-' }}</text>
  63. </view>
  64. <view class="detail-item">
  65. <text class="detail-label">商品数量</text>
  66. <text class="detail-value">{{ item.tradeNumber || '-' }}</text>
  67. </view>
  68. <!-- <view class="detail-item">
  69. <text class="detail-label">商品单价</text>
  70. <text class="detail-value">{{ item.tradePrice || '-' }}</text>
  71. </view>
  72. <view class="detail-item">
  73. <text class="detail-label">商品总价</text>
  74. <text class="detail-value">{{ item.priceSum || '-' }}</text>
  75. </view> -->
  76. </view>
  77. </view>
  78. </view>
  79. <view class="section-wrap">
  80. <view class="section-title">入库详情</view>
  81. <view v-for="(item, index) in inputForm.wareHouse" :key="item.id || index" class="detail-card">
  82. <view class="detail-card-head">
  83. <view class="detail-card-title">入库明细 {{ index + 1 }}</view>
  84. </view>
  85. <!-- <u-form-item label="入库人" :prop="'wareHouse[' + index + '].wareHouseMan'" :required="true">
  86. <u--input v-model="inputForm.wareHouse[index].wareHouseMan" placeholder="请选择入库人"
  87. :disabled="nodeFlag" @focus="openUserPullForm(index)" clearable></u--input>
  88. </u-form-item>
  89. <u-form-item label="入库部门" :prop="'wareHouse[' + index + '].wareHouseManOffice'">
  90. <u--input v-model="inputForm.wareHouse[index].wareHouseManOffice" disabled></u--input>
  91. </u-form-item> -->
  92. <ware-house-goods-selector :rowIndex="index" :inputForm="inputForm" :disabled="nodeFlag"
  93. @selected="handleGoodsSelected"></ware-house-goods-selector>
  94. <u-form-item label="商品数量" :prop="'wareHouse[' + index + '].tradeNumber'" :required="true" labelWidth="68px">
  95. <u--input v-model="inputForm.wareHouse[index].tradeNumber" placeholder="请输入商品数量"
  96. :disabled="nodeFlag" @blur="onRowInputBlur(index, 'tradeNumber')"></u--input>
  97. </u-form-item>
  98. <u-form-item label="品牌" :prop="'wareHouse[' + index + '].brand'" labelWidth="44px">
  99. <u--input v-model="inputForm.wareHouse[index].brand" disabled></u--input>
  100. </u-form-item>
  101. <u-form-item label="规格" :prop="'wareHouse[' + index + '].specification'" labelWidth="44px">
  102. <u--input v-model="inputForm.wareHouse[index].specification" disabled></u--input>
  103. </u-form-item>
  104. <u-form-item label="单位" :prop="'wareHouse[' + index + '].company'" labelWidth="44px">
  105. <u--input v-model="inputForm.wareHouse[index].company" placeholder="" disabled
  106. @blur="markWareHouseModified(index)"></u--input>
  107. </u-form-item>
  108. <!-- <u-form-item label="包装规格" :prop="'wareHouse[' + index + '].spec'">
  109. <u--input v-model="inputForm.wareHouse[index].spec" placeholder="请输入包装规格" :disabled="nodeFlag"
  110. @blur="onRowInputBlur(index, 'spec')"></u--input>
  111. </u-form-item> -->
  112. <!-- <u-form-item label="商品单价(元)" :prop="'wareHouse[' + index + '].tradePrice'">
  113. <u--input v-model="inputForm.wareHouse[index].tradePrice" placeholder="请输入商品单价"
  114. :disabled="nodeFlag" @blur="onRowInputBlur(index, 'tradePrice')"></u--input>
  115. </u-form-item>
  116. <u-form-item label="商品总价" :prop="'wareHouse[' + index + '].priceSum'">
  117. <u--input v-model="inputForm.wareHouse[index].priceSum" placeholder="自动计算" disabled></u--input>
  118. </u-form-item>
  119. <u-form-item label="生产日期" :prop="'wareHouse[' + index + '].produceDate'">
  120. <el-date-picker v-model="inputForm.wareHouse[index].produceDate" type="date"
  121. placeholder="请选择生产日期" style="width:100%" placement="bottom-start" clearable
  122. :disabled="nodeFlag" @change="markWareHouseModified(index)">
  123. </el-date-picker>
  124. </u-form-item>
  125. <u-form-item label="保质期" :prop="'wareHouse[' + index + '].shelfLife'">
  126. <view class="shelf-life-wrap">
  127. <u--input class="shelf-life-input" v-model="inputForm.wareHouse[index].shelfLife"
  128. placeholder="请输入保质期" :disabled="nodeFlag" @blur="markWareHouseModified(index)">
  129. </u--input>
  130. <jp-picker class="shelf-life-unit" placeholder="单位"
  131. v-model="inputForm.wareHouse[index].shelfLifeUnit" :disabled="nodeFlag" rangeKey="label"
  132. rangeValue="value" @change="markWareHouseModified(index)" :range="shelfLifeUnitOptions">
  133. </jp-picker>
  134. </view>
  135. </u-form-item>
  136. <u-form-item label="实际价格" :prop="'wareHouse[' + index + '].actualPrice'">
  137. <u--input v-model="inputForm.wareHouse[index].actualPrice" placeholder="请输入实际价格"
  138. :disabled="nodeFlag" @blur="onRowInputBlur(index, 'actualPrice')"></u--input>
  139. </u-form-item> -->
  140. <u-form-item label="备注" :prop="'wareHouse[' + index + '].remarks'" labelWidth="44px">
  141. <u--input v-model="inputForm.wareHouse[index].remarks" placeholder="请输入备注" :disabled="nodeFlag"
  142. @blur="markWareHouseModified(index)"></u--input>
  143. </u-form-item>
  144. <u-form-item label="附件上传" v-if="false">
  145. <UploadComponent :uploadUrl="`${uploadUrl}/public-modules-server/oss/file/webUpload/upload`"
  146. @onRemove="(file, fileList, fileIndex) => handleRemove(file, fileList, index, fileIndex, 'detail')"
  147. @onSuccess="(file, fileList) => handleUploadSuccess(file, fileList, index, 'detail')"
  148. :fileList="inputForm.wareHouse[index].fileInfoLost" :limit="3" :isDelete="nodeFlag"
  149. :isUpload="nodeFlag">
  150. </UploadComponent>
  151. </u-form-item>
  152. <u-form-item label="" v-if="!nodeFlag">
  153. <view class="row-action-group">
  154. <el-button style="width: 100%" type="danger" plain @click="removeWareHouseRow(index)">
  155. 删除明细
  156. </el-button>
  157. </view>
  158. </u-form-item>
  159. </view>
  160. <u-form-item label="" v-if="!nodeFlag">
  161. <el-button style="width: 100%" type="primary" @click="addWareHouseRow()" plain>新增入库明细</el-button>
  162. </u-form-item>
  163. </view>
  164. <u-form-item label="修改原因" borderBottom prop="updateCause" :required="true">
  165. <u--textarea placeholder="请填写修改原因" :rows="5" :maxlength="500" v-model="inputForm.updateCause"
  166. :disabled="nodeFlag"></u--textarea>
  167. </u-form-item>
  168. <u-form-item label="附件" v-if="false">
  169. <UploadComponent :uploadUrl="`${uploadUrl}/public-modules-server/oss/file/webUpload/upload`"
  170. @onRemove="(file, fileList, fileIndex) => handleRemove(file, fileList, '', fileIndex, '')"
  171. @onSuccess="(file, fileList) => handleUploadSuccess(file, fileList, '', '')"
  172. :fileList="inputForm.files" :limit="3" :isDelete="nodeFlag" :isUpload="nodeFlag">
  173. </UploadComponent>
  174. </u-form-item>
  175. </u--form>
  176. <user-select ref="userPicker" @input="handleUserSelected"></user-select>
  177. <ba-tree-picker ref="treePicker" :multiple="false" @select-change="selectTypeChange" title="类型选择"
  178. :localdata="listData" valueKey="value" textKey="label" childrenKey="children" />
  179. </view>
  180. </template>
  181. <script>
  182. import {
  183. mapState
  184. } from 'vuex'
  185. import userSelect from '@/components/user-select/user-select-radio.vue'
  186. import baTreePicker from '@/components/ba-tree-picker/ba-tree-picker.vue'
  187. import UploadComponent from '@/pages/common/UploadComponent.vue'
  188. import upload from '@/utils/upload.js'
  189. import MaterialTypeService from '@/api/psi/MaterialTypeService'
  190. import MaterialManagementService from '@/api/psi/MaterialManagementService'
  191. import WareHouseService from '@/api/psi/WareHouseService'
  192. import CommonApi from '@/api/common/CommonApi'
  193. import PurchaseSelector from './PurchaseSelector.vue'
  194. import WareHouseGoodsSelector from './WareHouseGoodsSelector.vue'
  195. export default {
  196. name: 'PsiWareHouseUpdateForm',
  197. components: {
  198. userSelect,
  199. baTreePicker,
  200. UploadComponent,
  201. PurchaseSelector,
  202. WareHouseGoodsSelector
  203. },
  204. computed: mapState({
  205. userInfo: (state) => state.user.userInfo
  206. }),
  207. props: {
  208. businessId: {
  209. type: String,
  210. default: ''
  211. },
  212. formReadOnly: {
  213. type: Boolean,
  214. default: false
  215. },
  216. status: {
  217. type: String,
  218. default: ''
  219. },
  220. isCc: {
  221. type: Boolean,
  222. default: false
  223. },
  224. },
  225. data() {
  226. return {
  227. uploadUrl: upload.UPLOAD_URL,
  228. nodeFlag: false,
  229. loading: false,
  230. listData: [],
  231. materialList: [],
  232. currentTypeIndex: -1,
  233. originalWareHouseRows: [],
  234. inputForm: this.createInputForm(),
  235. rules: {
  236. wareHouseName: [{
  237. required: true,
  238. message: '入库名称不能为空',
  239. trigger: ['blur', 'change']
  240. }],
  241. updateCause: [{
  242. required: true,
  243. message: '修改原因不能为空',
  244. trigger: ['blur', 'change']
  245. }]
  246. },
  247. shelfLifeUnitOptions: [{
  248. label: '年',
  249. value: '年'
  250. },
  251. {
  252. label: '月',
  253. value: '月'
  254. },
  255. {
  256. label: '天',
  257. value: '天'
  258. }
  259. ]
  260. }
  261. },
  262. materialTypeService: null,
  263. materialManagementService: null,
  264. wareHouseService: null,
  265. commonApi: null,
  266. created() {
  267. this.ensureServices()
  268. this.loadMaterialTypes()
  269. this.fillUserInfo()
  270. },
  271. watch: {
  272. businessId: {
  273. handler() {
  274. if (this.businessId) {
  275. this.init(this.businessId)
  276. } else {
  277. this.resetForm()
  278. this.nodeFlag = false
  279. }
  280. },
  281. immediate: true,
  282. deep: false
  283. }
  284. },
  285. async onLoad(options = {}) {
  286. const id = options.id || options.businessId || ''
  287. if (!id) {
  288. return
  289. }
  290. await this.init(id)
  291. if (options.readOnly === 'true' || options.method === 'view') {
  292. this.nodeFlag = true
  293. }
  294. },
  295. methods: {
  296. createInputForm() {
  297. return {
  298. id: '',
  299. procInsId: '',
  300. processDefinitionId: '',
  301. status: '',
  302. wareHouseTotalPrice: '',
  303. wareHouseActualPrice: '',
  304. tradeTotalPrice: '',
  305. fileInfoLost: [],
  306. purchaseNo: '',
  307. purchaseId: '',
  308. handledBy: '',
  309. handledById: '',
  310. handledByOffice: '',
  311. handledByOfficeName: '',
  312. wareHouseName: '',
  313. wareHouseNumber: '',
  314. userId: '',
  315. wareHouseDate: '',
  316. remarks: '',
  317. detailInfos: [],
  318. wareHouse: [],
  319. files: [],
  320. functionFlag: '',
  321. updateCause: ''
  322. }
  323. },
  324. ensureServices() {
  325. if (!this.materialTypeService) {
  326. this.materialTypeService = new MaterialTypeService()
  327. }
  328. if (!this.materialManagementService) {
  329. this.materialManagementService = new MaterialManagementService()
  330. }
  331. if (!this.wareHouseService) {
  332. this.wareHouseService = new WareHouseService()
  333. }
  334. if (!this.commonApi) {
  335. this.commonApi = new CommonApi()
  336. }
  337. },
  338. async loadMaterialTypes() {
  339. try {
  340. const data = await this.materialTypeService.cgList()
  341. this.materialList = data || []
  342. this.listData = this.buildTree(this.materialList)
  343. } catch (e) {
  344. this.materialList = []
  345. this.listData = []
  346. }
  347. },
  348. fillUserInfo() {
  349. if (!this.userInfo) {
  350. return
  351. }
  352. this.inputForm.handledBy = this.inputForm.handledBy || this.userInfo.name
  353. this.inputForm.handledById = this.inputForm.handledById || this.userInfo.id
  354. this.inputForm.userId = this.inputForm.userId || this.userInfo.id
  355. this.inputForm.handledByOffice = this.inputForm.handledByOffice || (this.userInfo.officeDTO && this
  356. .userInfo.officeDTO.id) || ''
  357. this.inputForm.handledByOfficeName = this.inputForm.handledByOfficeName || (this.userInfo.officeDTO && this
  358. .userInfo.officeDTO.name) || ''
  359. },
  360. resetForm() {
  361. this.inputForm = this.createInputForm()
  362. this.originalWareHouseRows = []
  363. this.fillUserInfo()
  364. },
  365. async init(id) {
  366. this.ensureServices()
  367. this.resetForm()
  368. this.inputForm.id = id
  369. this.loading = true
  370. try {
  371. let data = await this.wareHouseService.findById(id, this.status)
  372. if (data && data.status === '5' && this.status === 'taskFormDetail') {
  373. try {
  374. data = await this.wareHouseService.findLastHiByid(id)
  375. } catch (e) {}
  376. }
  377. this.inputForm = this.normalizeFormData(this.recover(this.createInputForm(), data))
  378. if (this.isEmpty(this.inputForm.id)) {
  379. this.inputForm.id = id
  380. }
  381. this.originalWareHouseRows = this.cloneRows(this.inputForm.wareHouse)
  382. this.fillUserInfo()
  383. this.nodeFlag = await this.resolveNodeFlag(data)
  384. this.calculateDetailTotalPrice()
  385. this.calculateWareHouseTotals()
  386. } catch (e) {
  387. this.nodeFlag = true
  388. } finally {
  389. this.loading = false
  390. }
  391. },
  392. async resolveNodeFlag(data) {
  393. if (this.formReadOnly || this.status === 'taskFormDetail' || this.status === 'testSee') {
  394. return true
  395. }
  396. try {
  397. const taskName = await this.commonApi.getTaskNameByProcInsId((data && data.procInsId) || this
  398. .inputForm.procInsId)
  399. if (this.isNotEmpty(taskName)) {
  400. return taskName !== '发起人重新发起申请'
  401. }
  402. } catch (e) {}
  403. return false
  404. },
  405. normalizeFormData(data) {
  406. const form = data || this.createInputForm()
  407. if (this.isNotEmpty(form.wareHouseDate)) {
  408. form.wareHouseDate = this.formatDate(form.wareHouseDate)
  409. }
  410. form.detailInfos = (form.detailInfos || []).map(item => ({
  411. ...item,
  412. fileInfoLost: item.fileInfoLost || [],
  413. priceSum: this.computePriceSum(item.tradePrice, item.tradeNumber)
  414. }))
  415. form.wareHouse = (form.wareHouse || [])
  416. .filter(item => item.upFlag !== '1' && item.upFlag !== '2')
  417. .map(item => ({
  418. ...item,
  419. fileInfoLost: item.fileInfoLost || [],
  420. upFlag: item.upFlag || '0',
  421. produceDate: this.isNotEmpty(item.produceDate) ? new Date(item.produceDate) : '',
  422. priceSum: this.computePriceSum(item.tradePrice, item.tradeNumber)
  423. }))
  424. return form
  425. },
  426. cloneRows(rows) {
  427. return JSON.parse(JSON.stringify(rows || []))
  428. },
  429. buildTree(nodes, parentId = '0') {
  430. const tree = []
  431. for (const node of nodes || []) {
  432. if (String(node.infoType || node.info_type || '') === '1') {
  433. continue
  434. }
  435. if (node.parentId === parentId) {
  436. const children = this.buildTree(nodes, node.id)
  437. if (children.length) {
  438. node.children = children
  439. }
  440. tree.push(node)
  441. }
  442. }
  443. return tree
  444. },
  445. isEmpty(value) {
  446. if (value === null || value === undefined) {
  447. return true
  448. }
  449. if (typeof value === 'string' && value.trim() === '') {
  450. return true
  451. }
  452. if (Array.isArray(value) && value.length === 0) {
  453. return true
  454. }
  455. return false
  456. },
  457. isNotEmpty(value) {
  458. return !this.isEmpty(value)
  459. },
  460. resolveBusinessId() {
  461. return this.inputForm.id || this.businessId || ''
  462. },
  463. ensureBusinessId() {
  464. const id = this.resolveBusinessId()
  465. if (this.isNotEmpty(id) && this.isEmpty(this.inputForm.id)) {
  466. this.inputForm.id = id
  467. }
  468. return id
  469. },
  470. computePriceSum(price, number) {
  471. if (this.isEmpty(price) || this.isEmpty(number)) {
  472. return ''
  473. }
  474. const amount = parseFloat(price || 0) * parseFloat(number || 0)
  475. if (Number.isNaN(amount)) {
  476. return ''
  477. }
  478. return amount.toFixed(2)
  479. },
  480. calculateDetailTotalPrice() {
  481. const total = (this.inputForm.detailInfos || []).reduce((sum, item) => {
  482. return sum + parseFloat(item.priceSum || 0)
  483. }, 0)
  484. this.inputForm.tradeTotalPrice = total ? total.toFixed(2) : ''
  485. },
  486. calculateWareHouseTotals() {
  487. const totalPrice = (this.inputForm.wareHouse || []).reduce((sum, item) => {
  488. return sum + parseFloat(item.priceSum || 0)
  489. }, 0)
  490. const totalActual = (this.inputForm.wareHouse || []).reduce((sum, item) => {
  491. return sum + parseFloat(item.actualPrice || 0)
  492. }, 0)
  493. this.inputForm.wareHouseTotalPrice = totalPrice ? totalPrice.toFixed(2) : ''
  494. this.inputForm.wareHouseActualPrice = totalActual ? totalActual.toFixed(2) : ''
  495. },
  496. getOriginalWareHouseRow(row) {
  497. const rowId = row && row.id
  498. if (this.isEmpty(rowId)) {
  499. return null
  500. }
  501. return (this.originalWareHouseRows || []).find(item => item.id === rowId) || null
  502. },
  503. isWareHouseRowChanged(row, originalRow) {
  504. if (!row || !originalRow) {
  505. return false
  506. }
  507. const fields = [
  508. 'wareHouseManId',
  509. 'wareHouseMan',
  510. 'deptId',
  511. 'wareHouseManOffice',
  512. 'wareHouseTypeId',
  513. 'wareHouseType',
  514. 'tradeName',
  515. 'tradeNumber',
  516. 'company',
  517. 'brand',
  518. 'specification',
  519. 'spec',
  520. 'tradePrice',
  521. 'actualPrice',
  522. 'produceDate',
  523. 'shelfLife',
  524. 'shelfLifeUnit',
  525. 'remarks'
  526. ]
  527. return fields.some(field => {
  528. let nextValue = row[field]
  529. let oldValue = originalRow[field]
  530. if (field === 'produceDate') {
  531. nextValue = this.formatDate(nextValue)
  532. oldValue = this.formatDate(oldValue)
  533. }
  534. return String(nextValue || '') !== String(oldValue || '')
  535. })
  536. },
  537. markChangedRowsForSubmit() {
  538. ;
  539. (this.inputForm.wareHouse || []).forEach((row, index) => {
  540. if (this.isEmpty(row.id) || this.isNotEmpty(row.sourceId)) {
  541. return
  542. }
  543. const originalRow = this.getOriginalWareHouseRow(row)
  544. if (this.isWareHouseRowChanged(row, originalRow)) {
  545. this.$set(this.inputForm.wareHouse[index], 'upFlag', '1')
  546. }
  547. })
  548. },
  549. normalizeWareHouseAmounts() {
  550. ;
  551. (this.inputForm.wareHouse || []).forEach((row, index) => {
  552. this.$set(this.inputForm.wareHouse[index], 'priceSum', this.computePriceSum(row.tradePrice, row
  553. .tradeNumber))
  554. })
  555. this.calculateWareHouseTotals()
  556. },
  557. openUserPullForm(index) {
  558. if (this.nodeFlag) {
  559. return
  560. }
  561. this.$refs.userPicker.open(index, 'wareHouse')
  562. },
  563. handleUserSelected(data, index, type) {
  564. if (type !== 'wareHouse') {
  565. return
  566. }
  567. this.patchWareHouseRow(index, {
  568. wareHouseManId: data.id,
  569. wareHouseMan: data.label,
  570. wareHouseManOffice: data.parentLabel,
  571. deptId: data.parentId
  572. })
  573. },
  574. openTypePicker(index) {
  575. if (this.nodeFlag) {
  576. return
  577. }
  578. this.currentTypeIndex = index
  579. this.$refs.treePicker._show(index)
  580. },
  581. selectTypeChange(ids, names, index) {
  582. this.patchWareHouseRow(index, {
  583. wareHouseType: names,
  584. wareHouseTypeId: ids[0],
  585. tradeName: '',
  586. tradePrice: '',
  587. tradeNumber: '',
  588. priceSum: '',
  589. company: '',
  590. brand: '',
  591. specification: '',
  592. spec: '1',
  593. actualPrice: '',
  594. surplusNumber: ''
  595. })
  596. },
  597. async handlePurchaseSelected(row) {
  598. try {
  599. const data = await this.materialManagementService.findById(row.id)
  600. if (this.isNotEmpty(this.inputForm.purchaseNo) && this.inputForm.purchaseNo !== data.purchaseNo) {
  601. this.inputForm.wareHouse = []
  602. }
  603. this.inputForm.purchaseNo = data.purchaseNo || row.purchaseNo
  604. this.inputForm.purchaseId = row.id
  605. this.inputForm.detailInfos = (data.detailInfos || []).map(item => ({
  606. ...item,
  607. fileInfoLost: item.fileInfoLost || [],
  608. priceSum: this.computePriceSum(item.tradePrice, item.tradeNumber)
  609. }))
  610. this.calculateDetailTotalPrice()
  611. this.calculateWareHouseTotals()
  612. } catch (e) {
  613. uni.showToast({
  614. title: '采购详情加载失败',
  615. icon: 'none'
  616. })
  617. }
  618. },
  619. handleGoodsSelected({
  620. index,
  621. item
  622. }) {
  623. const tradeName = item.tradeName || item.name || ''
  624. const goodsId = item.id || ''
  625. const exists = this.inputForm.wareHouse.some((detail, detailIndex) => {
  626. if (String(detailIndex) === String(index)) {
  627. return false
  628. }
  629. if (goodsId) {
  630. return String(detail.wareHouseTypeId || '') === String(goodsId)
  631. }
  632. return String(detail.tradeName || '') === String(tradeName || '')
  633. })
  634. if (exists) {
  635. uni.showToast({
  636. title: '同种商品只能选择一次',
  637. icon: 'none'
  638. })
  639. return
  640. }
  641. this.patchWareHouseRow(index, {
  642. wareHouseType: tradeName,
  643. wareHouseTypeId: item.id || '',
  644. tradeName,
  645. goodsName: tradeName,
  646. brand: item.brand || '',
  647. specification: item.specification || '',
  648. company: item.company || '',
  649. spec: '1'
  650. })
  651. },
  652. createWareHouseRow() {
  653. return {
  654. id: '',
  655. wareHouseManId: this.userInfo && this.userInfo.id,
  656. wareHouseMan: this.userInfo && this.userInfo.name,
  657. wareHouseManOffice: this.userInfo && this.userInfo.officeDTO && this.userInfo.officeDTO.name,
  658. deptId: this.userInfo && this.userInfo.officeDTO && this.userInfo.officeDTO.id,
  659. wareHouseType: '',
  660. wareHouseTypeId: '',
  661. tradeName: '',
  662. tradeNumber: '',
  663. company: '',
  664. brand: '',
  665. specification: '',
  666. spec: '1',
  667. tradePrice: '',
  668. priceSum: '',
  669. produceDate: '',
  670. shelfLife: '',
  671. shelfLifeUnit: '',
  672. actualPrice: '',
  673. remarks: '',
  674. fileInfoLost: [],
  675. upFlag: '0',
  676. sourceId: ''
  677. }
  678. },
  679. addWareHouseRow() {
  680. this.inputForm.wareHouse.push(this.createWareHouseRow())
  681. this.calculateWareHouseTotals()
  682. },
  683. patchWareHouseRow(index, patch, markChanged = true) {
  684. const row = (this.inputForm.wareHouse || [])[index] || {}
  685. const nextRow = {
  686. ...row,
  687. ...patch
  688. }
  689. if (markChanged) {
  690. if (this.isNotEmpty(row.id) && this.isEmpty(row.sourceId)) {
  691. nextRow.upFlag = '1'
  692. } else {
  693. nextRow.upFlag = row.upFlag || '0'
  694. }
  695. }
  696. if (Object.prototype.hasOwnProperty.call(patch, 'tradePrice') || Object.prototype.hasOwnProperty.call(
  697. patch, 'tradeNumber')) {
  698. nextRow.priceSum = this.computePriceSum(nextRow.tradePrice, nextRow.tradeNumber)
  699. }
  700. this.$set(this.inputForm.wareHouse, index, nextRow)
  701. this.calculateWareHouseTotals()
  702. },
  703. markWareHouseModified(index) {
  704. const row = (this.inputForm.wareHouse || [])[index]
  705. if (!row) {
  706. return
  707. }
  708. if (this.isNotEmpty(row.id) && this.isEmpty(row.sourceId) && row.upFlag !== '1') {
  709. this.$set(this.inputForm.wareHouse[index], 'upFlag', '1')
  710. }
  711. },
  712. removeWareHouseRow(index) {
  713. this.inputForm.wareHouse.splice(index, 1)
  714. this.calculateWareHouseTotals()
  715. },
  716. onRowInputBlur(index, field) {
  717. const row = (this.inputForm.wareHouse || [])[index]
  718. if (!row) {
  719. return
  720. }
  721. let value = row[field]
  722. if (['tradeNumber', 'tradePrice', 'actualPrice'].includes(field)) {
  723. value = this.formatNumberInput(value)
  724. }
  725. if (field === 'spec') {
  726. value = this.formatIntegerInput(value)
  727. }
  728. this.patchWareHouseRow(index, {
  729. [field]: value
  730. })
  731. },
  732. formatNumberInput(inputValue, decimalLimit = 2) {
  733. if (this.isEmpty(inputValue)) {
  734. return ''
  735. }
  736. const valueText = String(inputValue)
  737. if (!/^\d*\.?\d*$/.test(valueText)) {
  738. return ''
  739. }
  740. let value = valueText.replace(/[^\d.]/g, '')
  741. const dotIndex = value.indexOf('.')
  742. if (dotIndex !== -1) {
  743. const substr = value.substr(dotIndex + 1)
  744. if (substr.indexOf('.') !== -1) {
  745. value = value.substr(0, dotIndex + 1) + substr.replace(/\./g, '')
  746. }
  747. }
  748. if (dotIndex !== -1) {
  749. const integerPart = value.substring(0, dotIndex)
  750. const decimalPart = value.substring(dotIndex + 1, dotIndex + 1 + decimalLimit)
  751. value = integerPart + '.' + decimalPart
  752. }
  753. return value
  754. },
  755. formatIntegerInput(inputValue) {
  756. if (this.isEmpty(inputValue)) {
  757. return ''
  758. }
  759. return String(inputValue).replace(/[^\d]/g, '')
  760. },
  761. handleUploadSuccess(file, fileList, index, type) {
  762. if (type === 'detail') {
  763. this.inputForm.wareHouse[index].fileInfoLost = fileList
  764. this.markWareHouseModified(index)
  765. } else {
  766. this.inputForm.files = fileList
  767. }
  768. },
  769. handleRemove(file, fileList, lineIndex, fileIndex, type) {
  770. if (type === 'detail') {
  771. this.inputForm.wareHouse[lineIndex].fileInfoLost.splice(fileIndex, 1)
  772. this.markWareHouseModified(lineIndex)
  773. } else {
  774. this.inputForm.files.splice(fileIndex, 1)
  775. }
  776. },
  777. formatDate(date) {
  778. if (this.isEmpty(date)) {
  779. return ''
  780. }
  781. const dateNew = new Date(date)
  782. if (Number.isNaN(dateNew.getTime())) {
  783. return ''
  784. }
  785. const year = dateNew.getFullYear()
  786. const month = `${dateNew.getMonth() + 1}`.padStart(2, '0')
  787. const day = `${dateNew.getDate()}`.padStart(2, '0')
  788. return `${year}-${month}-${day}`
  789. },
  790. toSubmitData() {
  791. const businessId = this.ensureBusinessId()
  792. this.normalizeWareHouseAmounts()
  793. this.markChangedRowsForSubmit()
  794. const data = JSON.parse(JSON.stringify(this.inputForm))
  795. data.id = data.id || businessId
  796. data.wareHouseDate = this.formatDate(this.inputForm.wareHouseDate)
  797. data.tradeTotalPrice = this.normalizeOptionalNumber(data.tradeTotalPrice)
  798. data.wareHouseTotalPrice = this.normalizeOptionalNumber(data.wareHouseTotalPrice)
  799. data.wareHouseActualPrice = this.normalizeOptionalNumber(data.wareHouseActualPrice)
  800. data.wareHouse = (this.inputForm.wareHouse || []).map(item => ({
  801. ...JSON.parse(JSON.stringify(item)),
  802. produceDate: this.formatDate(item.produceDate),
  803. priceSum: this.normalizeOptionalNumber(this.computePriceSum(item.tradePrice, item
  804. .tradeNumber)),
  805. tradeTotalPrice: this.normalizeOptionalNumber(item.tradeTotalPrice),
  806. actualPrice: this.normalizeOptionalNumber(item.actualPrice)
  807. }))
  808. return data
  809. },
  810. normalizeOptionalNumber(value) {
  811. if (this.isEmpty(value)) {
  812. return null
  813. }
  814. return value
  815. },
  816. validateBaseForm() {
  817. if (this.isEmpty(this.inputForm.wareHouseDate)) {
  818. this.$message.error('入库时间不能为空')
  819. return false
  820. }
  821. return true
  822. },
  823. validateBusinessId() {
  824. if (this.isEmpty(this.ensureBusinessId())) {
  825. this.$message.error('入库数据ID为空,请返回列表重新进入')
  826. return false
  827. }
  828. return true
  829. },
  830. validateWareHouseRows() {
  831. if (this.isEmpty(this.inputForm.wareHouse)) {
  832. return true
  833. }
  834. const rows = this.inputForm.wareHouse
  835. for (let i = 0; i < rows.length; i++) {
  836. const row = rows[i]
  837. const lineNo = i + 1
  838. if (this.isEmpty(row.wareHouseMan)) {
  839. this.$message.error(`入库详情第${lineNo}行请选择入库人`)
  840. return false
  841. }
  842. if (this.isEmpty(row.wareHouseType)) {
  843. this.$message.error(`入库详情第${lineNo}行请选择入库类型`)
  844. return false
  845. }
  846. if (this.isEmpty(row.tradeName)) {
  847. this.$message.error(`入库详情第${lineNo}行请填写商品名称`)
  848. return false
  849. }
  850. if (this.isEmpty(row.tradeNumber)) {
  851. this.$message.error(`入库详情第${lineNo}行请填写商品数量`)
  852. return false
  853. }
  854. }
  855. for (let i = 0; i < rows.length; i++) {
  856. for (let j = i + 1; j < rows.length; j++) {
  857. if (rows[i].wareHouseTypeId === rows[j].wareHouseTypeId &&
  858. rows[i].tradeName === rows[j].tradeName) {
  859. this.$message.warning('入库详情中,同种入库类型的商品名称只能输入一条')
  860. return false
  861. }
  862. }
  863. }
  864. return true
  865. },
  866. async saveForm(callback) {
  867. try {
  868. if (!this.validateBaseForm()) {
  869. return
  870. }
  871. if (!this.validateBusinessId()) {
  872. return
  873. }
  874. await this.$refs.inputForm.validate()
  875. if (!this.validateWareHouseRows()) {
  876. return
  877. }
  878. this.loading = true
  879. this.calculateDetailTotalPrice()
  880. this.calculateWareHouseTotals()
  881. const submitData = this.toSubmitData()
  882. submitData.status = '1'
  883. submitData.functionFlag = ''
  884. this.inputForm.status = '1'
  885. const data = await this.wareHouseService.save(submitData)
  886. callback(data.businessTable, data.businessId, submitData)
  887. } finally {
  888. this.loading = false
  889. }
  890. },
  891. async startForm(callback) {
  892. this.loading = true
  893. try {
  894. const id = this.ensureBusinessId()
  895. if (this.isNotEmpty(id)) {
  896. const data = await this.wareHouseService.findById(id)
  897. if (this.isNotEmpty(data.status) && !['0', '1', '3', '5'].includes(data.status)) {
  898. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  899. throw new Error('invalid status')
  900. }
  901. }
  902. await this.startFormTrue(callback)
  903. } finally {
  904. this.loading = false
  905. }
  906. },
  907. async startFormTrue(callback) {
  908. if (!this.validateBaseForm()) {
  909. return
  910. }
  911. if (!this.validateBusinessId()) {
  912. return
  913. }
  914. await this.$refs.inputForm.validate()
  915. if (!this.validateWareHouseRows()) {
  916. return
  917. }
  918. this.calculateDetailTotalPrice()
  919. this.calculateWareHouseTotals()
  920. const submitData = this.toSubmitData()
  921. submitData.status = '2'
  922. submitData.functionFlag = 'start'
  923. this.inputForm.status = '2'
  924. this.inputForm.functionFlag = 'start'
  925. const data = await this.wareHouseService.save(submitData)
  926. this.inputForm.id = data.businessId
  927. callback(data.businessTable, data.businessId, submitData)
  928. },
  929. async reapplyForm(callback) {
  930. this.loading = true
  931. try {
  932. const id = this.ensureBusinessId()
  933. if (this.isEmpty(id)) {
  934. this.$message.error('入库数据ID为空,请返回列表重新进入')
  935. throw new Error('missing business id')
  936. }
  937. const data = await this.wareHouseService.findById(id)
  938. if (data.status !== '4') {
  939. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  940. throw new Error('invalid status')
  941. }
  942. await this.startFormTrue(callback)
  943. } finally {
  944. this.loading = false
  945. }
  946. },
  947. async agreeForm(callback) {
  948. this.loading = true
  949. try {
  950. if (!this.validateBaseForm()) {
  951. return
  952. }
  953. if (!this.validateBusinessId()) {
  954. return
  955. }
  956. await this.$refs.inputForm.validate()
  957. if (!this.validateWareHouseRows()) {
  958. return
  959. }
  960. this.calculateDetailTotalPrice()
  961. this.calculateWareHouseTotals()
  962. const submitData = this.toSubmitData()
  963. submitData.status = '5'
  964. submitData.functionFlag = 'agree'
  965. this.inputForm.status = '5'
  966. this.inputForm.functionFlag = 'agree'
  967. const data = await this.wareHouseService.save(submitData)
  968. callback(data.businessTable, data.businessId, submitData)
  969. } finally {
  970. this.loading = false
  971. }
  972. },
  973. async updateStatusById(type, callback) {
  974. this.loading = true
  975. try {
  976. const id = this.ensureBusinessId()
  977. if (this.isEmpty(id)) {
  978. this.$message.error('入库数据ID为空,请返回列表重新进入')
  979. throw new Error('missing business id')
  980. }
  981. if (type === 'reject' || type === 'reback') {
  982. const data = await this.wareHouseService.findById(id)
  983. if (data.status !== '2') {
  984. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  985. throw new Error('invalid status')
  986. }
  987. const status = type === 'reject' ? '4' : '3'
  988. this.inputForm.status = status
  989. await this.wareHouseService.updateStatusById({
  990. status,
  991. id
  992. })
  993. callback()
  994. } else if (type === 'hold') {
  995. const data = await this.wareHouseService.findById(id)
  996. if (data.status !== '4') {
  997. this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
  998. throw new Error('invalid status')
  999. }
  1000. await this.wareHouseService.backSourceData(id)
  1001. callback()
  1002. }
  1003. } finally {
  1004. this.loading = false
  1005. }
  1006. }
  1007. }
  1008. }
  1009. </script>
  1010. <style scoped>
  1011. .section-wrap {
  1012. margin-top: 24rpx;
  1013. }
  1014. .section-title {
  1015. margin-bottom: 16rpx;
  1016. padding-left: 8rpx;
  1017. font-size: 30rpx;
  1018. font-weight: 700;
  1019. color: #1f2937;
  1020. border-left: 6rpx solid #2979ff;
  1021. }
  1022. .detail-card {
  1023. margin-bottom: 20rpx;
  1024. padding: 24rpx;
  1025. background: #fff;
  1026. border-radius: 20rpx;
  1027. box-shadow: 0 10rpx 30rpx rgba(15, 23, 42, 0.05);
  1028. }
  1029. .detail-card-head {
  1030. display: flex;
  1031. align-items: center;
  1032. justify-content: space-between;
  1033. margin-bottom: 12rpx;
  1034. }
  1035. .detail-card-title {
  1036. font-size: 28rpx;
  1037. font-weight: 600;
  1038. color: #0f172a;
  1039. }
  1040. .detail-grid {
  1041. display: grid;
  1042. grid-template-columns: repeat(2, minmax(0, 1fr));
  1043. gap: 16rpx;
  1044. }
  1045. .detail-item {
  1046. display: flex;
  1047. flex-direction: column;
  1048. padding: 18rpx 20rpx;
  1049. background: #f8fafc;
  1050. border-radius: 16rpx;
  1051. }
  1052. .detail-label {
  1053. font-size: 22rpx;
  1054. color: #64748b;
  1055. }
  1056. .detail-value {
  1057. margin-top: 8rpx;
  1058. font-size: 26rpx;
  1059. line-height: 1.5;
  1060. color: #0f172a;
  1061. word-break: break-all;
  1062. }
  1063. .row-action-group {
  1064. display: flex;
  1065. gap: 16rpx;
  1066. width: 100%;
  1067. }
  1068. .shelf-life-wrap {
  1069. display: flex;
  1070. width: 100%;
  1071. gap: 16rpx;
  1072. }
  1073. .shelf-life-input {
  1074. flex: 1;
  1075. min-width: 0;
  1076. }
  1077. .shelf-life-unit {
  1078. flex: 0 0 128rpx;
  1079. width: 128rpx;
  1080. }
  1081. @media (max-width: 768px) {
  1082. .detail-grid {
  1083. grid-template-columns: repeat(1, minmax(0, 1fr));
  1084. }
  1085. }
  1086. </style>