salesReturnEdit.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. <template>
  2. <div class="salesReturnEdit-wrap">
  3. <a-spin :spinning="spinning" tip="Loading...">
  4. <a-page-header :ghost="false" :backIcon="false" class="salesReturnEdit-back" >
  5. <!-- 自定义的二级文字标题 -->
  6. <template slot="subTitle">
  7. <a id="salesReturnEdit-back-btn" href="javascript:;" @click="handleBack"><a-icon type="left" /> 返回列表</a>
  8. <span style="margin: 0 15px;color: #666;">客户名称:{{ ordeDetail&&ordeDetail.buyerName || '--' }}</span>
  9. </template>
  10. <!-- 操作区,位于 title 行的行尾 -->
  11. <template slot="extra">
  12. <a-button
  13. key="3"
  14. type="default"
  15. class="button-info"
  16. id="salesReturnEdit-preview-btn"
  17. v-if="$hasPermissions('B_salesReturnPrint')"
  18. :disabled="chooseLoadData.length==0"
  19. @click="handlePrint('preview')">打印预览</a-button>
  20. <a-button
  21. key="2"
  22. type="primary"
  23. class="button-info"
  24. id="salesReturnEdit-print-btn"
  25. v-if="$hasPermissions('B_salesReturnPrint')"
  26. :disabled="chooseLoadData.length==0"
  27. @click="handlePrint('print')">快捷打印</a-button>
  28. <a-divider type="vertical" />
  29. <a-button
  30. key="1"
  31. type="primary"
  32. class="button-warning"
  33. id="salesReturnEdit-export-btn"
  34. v-if="$hasPermissions('B_salesReturnExport')"
  35. :disabled="chooseLoadData.length==0"
  36. @click="handlePrint('export')">导出Excel</a-button>
  37. </template>
  38. </a-page-header>
  39. <a-card size="small" :bordered="false" class="salesReturnEdit-cont">
  40. <!-- 查询配件列表 -->
  41. <queryPart ref="partQuery" :newLoading="isInster" @add="saveProduct"></queryPart>
  42. </a-card>
  43. <a-card size="small" :bordered="false" class="salesReturnEdit-cont">
  44. <a-alert style="margin-bottom: 10px;" type="info">
  45. <div slot="message" class="total-bar">
  46. <div>
  47. <span>申请退货数量:{{ ordeDetail&&(ordeDetail.totalInitialQty || ordeDetail.totalInitialQty==0) ? ordeDetail.totalInitialQty : '--' }};</span>
  48. <span v-if="$hasPermissions('B_isShowPrice')">参考退货金额:{{ ordeDetail&&(ordeDetail.totalAmount || ordeDetail.totalAmount==0) ? toThousands(ordeDetail.totalAmount) : '--' }};</span>
  49. </div>
  50. </div>
  51. </a-alert>
  52. <div class="table-page-search-wrapper" style="display:flex;align-items: center;justify-content: space-between;">
  53. <div style="flex-grow:1;">
  54. <a-form layout="inline" @keyup.enter.native="$refs.table.refresh(true)">
  55. <a-row :gutter="15">
  56. <a-col :md="6" :sm="24">
  57. <a-form-item label="产品编码">
  58. <a-input v-model.trim="productForm.productCode" allowClear placeholder="请输入产品编码"/>
  59. </a-form-item>
  60. </a-col>
  61. <a-col :md="6" :sm="24">
  62. <a-form-item label="产品名称">
  63. <a-input v-model.trim="productForm.productName" allowClear placeholder="请输入产品名称"/>
  64. </a-form-item>
  65. </a-col>
  66. <a-col :md="6" :sm="24" style="margin-bottom: 10px;">
  67. <a-button type="primary" @click="$refs.table.refresh(true)" :disabled="disabled">查询</a-button>
  68. <a-button style="margin-left: 5px" @click="resetSearchForm" :disabled="disabled">重置</a-button>
  69. </a-col>
  70. </a-row>
  71. </a-form>
  72. </div>
  73. <div>
  74. <span style="margin-right: 10px;" v-if="selNums">已选{{ selNums }}项</span>
  75. <a-button type="default" class="button-info" @click="openPlSetReason">批量设置退货原因</a-button>
  76. <a-button type="primary" class="button-info" id="salesReturnEdit-import-btn" @click="openGuideModal=true">导入产品</a-button>
  77. </div>
  78. </div>
  79. <!-- 已选配件列表 -->
  80. <s-table
  81. class="sTable"
  82. ref="table"
  83. size="small"
  84. :rowKey="(record) => record.id"
  85. :row-selection="{ columnWidth: 40 }"
  86. @rowSelection="rowSelectionFun"
  87. :columns="columns"
  88. :data="loadData"
  89. :defaultLoadData="false"
  90. :scroll="{ y: 300 }"
  91. bordered>
  92. <!-- 本次退货数量 -->
  93. <template slot="qty" slot-scope="text, record">
  94. <a-input-number
  95. id="salesReturn-qty"
  96. size="small"
  97. v-model="record.initialQty"
  98. :precision="0"
  99. :min="0"
  100. :max="999999"
  101. placeholder="请输入"
  102. @blur="e => onCellBlur(e.target.value, record, 'qty')"
  103. style="width: 100%;" />
  104. </template>
  105. <!-- 退货原因 -->
  106. <template slot="returnReason" slot-scope="text, record">
  107. <returnReason v-model="record.returnReason" @blur="updateReason(record)"></returnReason>
  108. </template>
  109. <!-- 操作 -->
  110. <template slot="action" slot-scope="text, record">
  111. <a-button
  112. size="small"
  113. type="link"
  114. :loading="delLoading"
  115. class="button-error"
  116. @click="handleDel(record)"
  117. id="salesReturn-Del">删除</a-button>
  118. </template>
  119. </s-table>
  120. </a-card>
  121. </a-spin>
  122. <div class="affix-cont">
  123. <a-button
  124. size="large"
  125. style="padding: 0 60px;"
  126. :disabled="spinning"
  127. type="primary"
  128. class="button-primary"
  129. @click="beforeSubmit()"
  130. id="salesReturn-handleSubmit">提交</a-button>
  131. </div>
  132. <!-- 选择审核人员 -->
  133. <chooseDepartUserModal ref="chooseDepart" :openModal="openDepartUserModal" @close="openDepartUserModal=false" @submit="handleSubmit"></chooseDepartUserModal>
  134. <!-- 导入产品 -->
  135. <importGuideModal :openModal="openGuideModal" :params="{salesReturnBillSn: $route.params.sn}" @close="closeGuideModel" @ok="hanldeOk" />
  136. <!-- 批量设置退货原因 -->
  137. <commonModal modalTit="批量设置退货原因" :openModal="showPlModal" @cancel="showPlModal=false" @ok="setPlReturnReason">
  138. <div style="text-align: center;">
  139. <div style="margin-bottom: 15px;font-size: 14px;"><strong>请输入退货原因</strong></div>
  140. <div style="line-height: 24px;">
  141. <div><returnReason size="large" v-model="plReturnReason"></returnReason></div>
  142. </div>
  143. </div>
  144. </commonModal>
  145. </div>
  146. </template>
  147. <script>
  148. import { commonMixin } from '@/utils/mixin'
  149. import { STable, VSelect } from '@/components'
  150. import { printFun, exportExcel } from '@/libs/JGPrint.js'
  151. import { removeEmptyStr } from '@/libs/tools'
  152. import ImportGuideModal from './importGuideModal.vue'
  153. import queryPart from './queryPart.vue'
  154. import commonModal from '@/views/common/commonModal.vue'
  155. import EditableCell from '@/views/common/editInput.js'
  156. import chooseDepartUserModal from './chooseDepartUserModal.vue'
  157. import returnReason from '@/views/common/returnReason'
  158. import {
  159. salesReturnDetail,
  160. salesReturnSubmit,
  161. salesReturnBatchInsert,
  162. salesReturnPrint,
  163. salesReturnExport
  164. } from '@/api/salesReturn'
  165. import {
  166. salesReturnDetailList,
  167. salesReturnDetailDel,
  168. salesReturnDetailInsert,
  169. salesReturnDetailUpdateQty,
  170. salesReturnDetailUpdateReason,
  171. salesReturnDetailSetReason
  172. } from '@/api/salesReturnDetail'
  173. export default {
  174. name: 'SalesReturnEdit',
  175. mixins: [commonMixin],
  176. components: {
  177. STable,
  178. VSelect,
  179. queryPart,
  180. EditableCell,
  181. ImportGuideModal,
  182. chooseDepartUserModal,
  183. returnReason,
  184. commonModal
  185. },
  186. data () {
  187. return {
  188. spinning: false,
  189. orderId: null,
  190. orderSn: null,
  191. buyerSn: null,
  192. disabled: false,
  193. isInster: false, // 是否正在添加产品
  194. openDepartUserModal: false,
  195. ordeDetail: { discountAmount: 0 },
  196. delLoading: false,
  197. // 已选产品
  198. dataSource: [],
  199. productForm: {
  200. salesReturnBillSn: '',
  201. productName: '',
  202. productCode: ''
  203. },
  204. chooseLoadData: [],
  205. // 加载数据方法 必须为 Promise 对象
  206. loadData: parameter => {
  207. this.disabled = true
  208. // 查询总计
  209. this.productForm.salesReturnBillSn = this.$route.params.sn
  210. return salesReturnDetailList(Object.assign(parameter, this.productForm)).then(res => {
  211. let data
  212. if (res.status == 200) {
  213. data = res.data
  214. const no = (data.pageNo - 1) * data.pageSize
  215. for (var i = 0; i < data.list.length; i++) {
  216. data.list[i].no = no + i + 1
  217. data.list[i].qtyBackups = data.list[i].initialQty
  218. data.list[i].returnReasonBackups = data.list[i].returnReason
  219. }
  220. this.disabled = false
  221. this.chooseLoadData = data.list
  222. this.total = res.data.count
  223. }
  224. return data
  225. })
  226. },
  227. openGuideModal: false, // 导入产品引导
  228. total: 0,
  229. rowSelectionInfo: null,
  230. plReturnReason: '',
  231. showPlModal: false
  232. }
  233. },
  234. watch: {
  235. showPlModal (newValue, oldValue) {
  236. if (!newValue) {
  237. this.plReturnReason = ''
  238. }
  239. }
  240. },
  241. computed: {
  242. selNums () {
  243. return this.rowSelectionInfo && this.rowSelectionInfo.selectedRowKeys.length || 0
  244. },
  245. columns () {
  246. const arr = [
  247. { title: '序号', dataIndex: 'no', align: 'center', width: '4%' },
  248. { title: '产品编码', dataIndex: 'productEntity.code', width: '18%', align: 'center', customRender: function (text) { return text || '--' } },
  249. { title: '产品名称', dataIndex: 'productEntity.name', width: '25%', align: 'left', customRender: function (text) { return text || '--' }, ellipsis: true },
  250. { title: '单位', dataIndex: 'productEntity.unit', align: 'center', width: '6%', customRender: function (text) { return text || '--' } },
  251. { title: '申请退货数量', dataIndex: 'initialQty', align: 'center', width: '8%', scopedSlots: { customRender: 'qty' } },
  252. { title: '退货原因', dataIndex: 'returnReason', align: 'center', width: '13%', scopedSlots: { customRender: 'returnReason' } },
  253. { title: '操作', scopedSlots: { customRender: 'action' }, width: '10%', align: 'center' }
  254. ]
  255. if (this.$hasPermissions('B_isShowPrice')) { // 售价权限
  256. arr.splice(4, 0, { title: '参考退货单价', dataIndex: 'price', align: 'right', width: '8%', customRender: text => ((text || text == 0) ? this.toThousands(text) : '--') })
  257. arr.splice(7, 0, { title: '参考退货金额', align: 'right', dataIndex: 'totalAmount', width: '8%', customRender: text => ((text || text == 0) ? this.toThousands(text) : '--') })
  258. }
  259. return arr
  260. }
  261. },
  262. methods: {
  263. // 返回
  264. handleBack () {
  265. this.$router.push({ name: 'salesReturnList', query: { closeLastOldTab: true } })
  266. },
  267. // 表格选中项
  268. rowSelectionFun (obj) {
  269. this.rowSelectionInfo = obj || null
  270. },
  271. // 已选产品 blur
  272. onCellBlur (val, record, type) {
  273. const valBackups = record.qtyBackups
  274. // 光标移出,值发生改变再调用编辑接口
  275. if (val && val != valBackups) {
  276. this.spinning = true
  277. salesReturnDetailUpdateQty({
  278. salesReturnDetailSn: record.salesReturnDetailSn,
  279. initialQty: record.initialQty,
  280. price: record.price
  281. }).then(res => {
  282. if (res.status == 200) {
  283. this.resetTable(false)
  284. this.$message.success(res.message)
  285. this.spinning = false
  286. record.qtyBackups = record.initialQty
  287. } else {
  288. this.spinning = false
  289. record.initialQty = valBackups
  290. }
  291. })
  292. } else {
  293. record.initialQty = valBackups
  294. }
  295. },
  296. // 修改退货原因
  297. updateReason (row) {
  298. console.log(row.returnReason)
  299. row.returnReason = removeEmptyStr(row.returnReason)
  300. // 空或没有改变时不保存数据
  301. if (!row.returnReason || row.returnReason == row.returnReasonBackups) {
  302. row.returnReason = row.returnReasonBackups
  303. return
  304. }
  305. this.spinning = true
  306. salesReturnDetailUpdateReason({
  307. salesReturnDetailSn: row.salesReturnDetailSn,
  308. returnReason: row.returnReason
  309. }).then(res => {
  310. if (res.status == 200) {
  311. this.resetTable(false)
  312. this.$message.success(res.message)
  313. } else {
  314. row.returnReason = row.returnReasonBackups
  315. }
  316. this.spinning = false
  317. })
  318. },
  319. // 批量设置退货原因
  320. openPlSetReason () {
  321. if (this.selNums) {
  322. this.showPlModal = true
  323. } else {
  324. this.$message.warning('请先选择产品!')
  325. }
  326. },
  327. setPlReturnReason () {
  328. if (this.plReturnReason == '') {
  329. this.$message.warning('请输入退货原因!')
  330. } else {
  331. this.spinning = true
  332. const snList = []
  333. const arr = this.rowSelectionInfo && this.rowSelectionInfo.selectedRows || []
  334. arr.map(item => {
  335. snList.push(item.salesReturnDetailSn)
  336. })
  337. salesReturnDetailSetReason({
  338. salesReturnBillDetailSnList: snList,
  339. returnReason: this.plReturnReason
  340. }).then(res => {
  341. if (res.status == 200) {
  342. this.showPlModal = false
  343. this.$refs.table.clearSelected()
  344. this.resetTable(false)
  345. }
  346. this.spinning = false
  347. })
  348. }
  349. },
  350. // 获取单据详细
  351. getOrderDetail () {
  352. salesReturnDetail({ sn: this.orderSn }).then(res => {
  353. this.ordeDetail = res.data || null
  354. })
  355. },
  356. // 删除产品
  357. handleDel (row) {
  358. const _this = this
  359. this.$confirm({
  360. title: '提示',
  361. content: '确认要删除吗?',
  362. centered: true,
  363. closable: true,
  364. onOk () {
  365. _this.delLoading = true
  366. _this.spinning = true
  367. salesReturnDetailDel({ salesReturnDetailSn: row.salesReturnDetailSn }).then(res => {
  368. if (res.status == 200) {
  369. _this.resetTable(false)
  370. }
  371. _this.$message.info(res.message)
  372. _this.delLoading = false
  373. _this.spinning = false
  374. })
  375. }
  376. })
  377. },
  378. resetTable (flag) {
  379. this.$refs.table.refresh(flag)
  380. this.getOrderDetail()
  381. },
  382. // 重置
  383. resetSearchForm (flag) {
  384. this.productForm.productName = ''
  385. this.productForm.productCode = ''
  386. this.$refs.table.refresh(!!flag)
  387. this.getOrderDetail()
  388. },
  389. // 添加或修改产品
  390. saveProduct (row) {
  391. // 防止多次添加产品
  392. if (this.isInster) {
  393. return
  394. }
  395. this.isInster = true
  396. const params = {
  397. 'salesReturnBillSn': this.orderSn,
  398. 'salesReturnBillNo': this.ordeDetail.salesReturnBillNo,
  399. 'price': row.productPrice,
  400. 'cost': row.lastStockCost,
  401. 'returnReason': row.returnReason,
  402. 'productSn': row.productSn,
  403. 'initialQty': row.qty
  404. }
  405. this.spinning = true
  406. salesReturnDetailInsert(params).then(res => {
  407. if (res.status == 200) {
  408. this.resetSearchForm(true)
  409. this.$message.success(res.message)
  410. }
  411. this.isInster = false
  412. this.spinning = false
  413. })
  414. },
  415. beforeSubmit () {
  416. if (this.total) {
  417. const hasKong = this.chooseLoadData.find(item => !item.returnReason)
  418. if (hasKong) {
  419. this.$message.warning('请输入退货原因!')
  420. } else {
  421. this.openDepartUserModal = true
  422. if (this.ordeDetail.billStatus == 'AUDIT_REJECT') {
  423. this.$refs.chooseDepart.getDetail(this.ordeDetail)
  424. }
  425. }
  426. } else {
  427. this.$message.warning('请添加产品!')
  428. }
  429. },
  430. // 提交销售退货
  431. handleSubmit (data) {
  432. this.spinning = true
  433. salesReturnSubmit({ salesReturnBillSn: this.orderSn, ...data }).then(res => {
  434. if (res.status == 200) {
  435. this.handleBack()
  436. this.$message.success(res.message)
  437. }
  438. this.spinning = false
  439. })
  440. },
  441. closeGuideModel () {
  442. this.openGuideModal = false
  443. },
  444. // 导入产品
  445. hanldeOk (obj) {
  446. salesReturnBatchInsert(obj).then(res => {
  447. if (res.status == 200) {
  448. this.resetTable(true)
  449. }
  450. })
  451. },
  452. // 打印预览/快捷打印
  453. handlePrint (type) {
  454. const _this = this
  455. const a = ['WAIT_CUSTOMER_SERVICE_CONFIRM', 'WAIT_FINANCIAL_AUDIT', 'FINANCIAL_REJECT', 'FINISH'].find(item => item == this.ordeDetail.billStatus)
  456. const status = a ? 'SALES_RETURN_AMOUNT' : 'SALES_RETURN_REASON'
  457. const params = { salesReturnBillSn: this.$route.params.sn, priceType: status }
  458. _this.spinning = true
  459. // 导出
  460. if (type == 'export') {
  461. exportExcel(salesReturnExport, params, '销售退货', function () {
  462. _this.spinning = false
  463. })
  464. } else {
  465. // 打印或预览
  466. printFun(salesReturnPrint, params, type, '销售退货', () => {
  467. _this.spinning = false
  468. })
  469. }
  470. },
  471. pageInit () {
  472. this.orderSn = this.$route.params.sn
  473. this.buyerSn = this.$route.params.buyerSn
  474. this.resetSearchForm(true)
  475. this.$refs.partQuery.pageInit(this.buyerSn, 0)
  476. }
  477. },
  478. mounted () {
  479. if (!this.$store.state.app.isNewTab) { // 页签刷新时调用
  480. this.pageInit()
  481. }
  482. },
  483. activated () {
  484. // 如果是新页签打开或者进入新的子页(例:存在列表第2条数据编辑页页签时再打开第4条数据的编辑页),则重置当前页面
  485. if (this.$store.state.app.isNewTab || !this.$store.state.app.isNewSubTab) {
  486. this.pageInit()
  487. }
  488. },
  489. beforeRouteEnter (to, from, next) {
  490. next(vm => {})
  491. }
  492. }
  493. </script>
  494. <style lang="less">
  495. .salesReturnEdit-wrap{
  496. position: relative;
  497. height: 100%;
  498. padding-bottom: 51px;
  499. box-sizing: border-box;
  500. >.ant-spin-nested-loading{
  501. overflow-y: scroll;
  502. height: 100%;
  503. }
  504. .salesReturnEdit-cont{
  505. margin-top: 10px;
  506. .total-bar{
  507. display: flex;
  508. align-items: center;
  509. justify-content: space-between;
  510. > div{
  511. &:last-child{
  512. display: flex;
  513. align-items: center;
  514. justify-content: space-between;
  515. > div{
  516. padding: 0 10px;
  517. }
  518. }
  519. }
  520. }
  521. }
  522. }
  523. </style>