chooseProduct.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. <template>
  2. <view class="chooseProduct-wrap">
  3. <!-- 产品列表 -->
  4. <view class="product-list-box">
  5. <scroll-view class="product-list-con" :style="{height: scrollH+'upx'}" scroll-y @scrolltolower="onreachBottom">
  6. <view class="product-list-item font_13" v-for="(item, index) in listData" :key="item.id">
  7. <view class="list-item-tit">
  8. <view class="item-tit ellipsis-one" :style="{borderColor: $config('primaryColor')}">{{item.productName}}</view>
  9. </view>
  10. <view class="list-item-con flex align_center justify_between">
  11. <view class="flex align_center justify_between flex_1">
  12. <view class="pimgs">
  13. <u-image :src="item.productMainImage?item.productMainImage:`../../static/${theme}/def_img@2x.png`" width="128" height="128" border-radius="10"></u-image>
  14. </view>
  15. <view class="flex_1">
  16. <view>产品编码:{{item.productCode || '--'}}</view>
  17. <view class="padding_3">原厂编码:{{item.productOrigCode || '--'}}</view>
  18. <view v-if="item.warehouseName || item.warehouseLocationName">仓库仓位:{{item.warehouseName}} > {{item.warehouseLocationName}}</view>
  19. </view>
  20. </view>
  21. <view class="list-item-btn">
  22. <u-icon class="item-btn-add" name="plus-circle" :color="$config('primaryColor')" size="42" @click="handleAdd(item, 'add')"></u-icon>
  23. </view>
  24. </view>
  25. <view class="list-item-footer flex align_center justify_between">
  26. <view class="footer-item" v-if="searchParams && searchParams.cost &&$hasPermissions('M_showCostPrice_mobile')">
  27. <view>{{toThousands(item.putCost||0, 2)}}</view>
  28. <view class="color_6">成本价</view>
  29. </view>
  30. <view class="footer-item" v-if="searchParams && searchParams.cityPrice">
  31. <view>{{toThousands(item.dealerProduct && item.dealerProduct.cityPrice || 0, 2)}}</view>
  32. <view class="color_6">市级价</view>
  33. </view>
  34. <view class="footer-item">
  35. <view>{{toThousands(item.currentQty||0)}}{{item.unit}}</view>
  36. <view class="color_6">库存数量</view>
  37. </view>
  38. <view class="footer-item">
  39. <view>
  40. <text :style="{color: $config('errorColor')}">¥{{toThousands(item.price||0, 2)}}</text>
  41. <text v-if="item.origSalePriceFlag == 1">(原)</text>
  42. </view>
  43. <view class="color_6">参考售价</view>
  44. </view>
  45. </view>
  46. </view>
  47. <view v-if="listData && listData.length == 0">
  48. <u-empty :src="`/static/${$config('themePath')}/def_no_data@3x.png`" icon-size="150" :text="noDataText" mode="list" :margin-top="50"></u-empty>
  49. </view>
  50. <view style="padding-bottom: 20upx;">
  51. <u-loadmore v-if="totalNum>listData.length || status=='loading'" :status="status" />
  52. </view>
  53. </scroll-view>
  54. </view>
  55. <!-- 合计 -->
  56. <view ref="productTotal" class="selected-box flex align_center justify_between">
  57. <view class="choose-info flex_1 flex align_center justify_between" @click="lookProduct">
  58. <text>已选 {{detailData && (detailData.totalCategory || detailData.totalCategory==0) ? toThousands(detailData.totalCategory) : '--'}} 款,合计 ¥{{detailData && (detailData.totalAmount || detailData.totalAmount==0) ? toThousands(detailData.totalAmount, 2) : '--'}}</text>
  59. <u-icon name="arrow-up" :color="$config('primaryColor')" size="34" class="close-circle"></u-icon>
  60. </view>
  61. <view class="choose-btn" @click="handleChoose" :style="{backgroundColor: $config('primaryColor')}">选好了</view>
  62. </view>
  63. <!-- 查询右侧弹框 -->
  64. <productSearch :openModal="openModal" :defaultParams="searchParams" @refresh="refresh" @close="openModal=false" />
  65. <!-- 添加/编辑产品 -->
  66. <productModal :openModal="openProductModal" :infoData="productInfo" @confirm="productConfirm" @close="openProductModal=false" />
  67. <!-- 已选产品 -->
  68. <selectedProductsModal ref="selectedProducts" :openModal="openSelectedModal" :nowData="detailData" @edit="handleEdit" @confirm="handleChoose" @delete="handleDel" @close="openSelectedModal=false" />
  69. <!-- 弹框 -->
  70. <common-modal :openModal="commonModal" :content="modalText" @confirm="handleCommon" @cancel="handleCancel" @close="commonModal=false" />
  71. </view>
  72. </template>
  73. <script>
  74. import ProductSearch from './productSearch.vue'
  75. import ProductModal from './productModal.vue'
  76. import SelectedProductsModal from './selectedProductsModal.vue'
  77. import commonModal from '@/pages/common/commonModal.vue'
  78. import { toThousands } from '@/libs/tools'
  79. import { querySumByProductLocation, stockByProductSn } from '@/api/stock'
  80. import { salesDetail, supperCodeByVin, salesDetailInsert, salesDetailUpdate, salesDetailList } from '@/api/sales'
  81. export default{
  82. components: { ProductSearch, ProductModal, SelectedProductsModal, commonModal },
  83. data(){
  84. return{
  85. toThousands,
  86. listData: [],
  87. pageNo: 1,
  88. pageSize: 6,
  89. totalNum: 0,
  90. status: 'loadmore',
  91. noDataText: '暂无数据',
  92. searchParams: null,
  93. openModal: false,
  94. theme: '',
  95. salesInfo: null, // 销售单信息
  96. scrollH: 0,
  97. openProductModal: false, // 编辑产品信息弹框是否显示
  98. productData: null, // 当前操作的产品信息
  99. productInfo: null, // 添加/编辑产品需要的参数
  100. chooseType: 'add', // 添加产品add,编辑产品edit
  101. commonModal: false, // 公共弹框是否显示
  102. modalText: '', // 公共弹框提示语
  103. commonType: 1, // 公共弹框类型 1为添加产品时库存不足,2为编辑产品时库存不足,3为售价低于定价,4为售价低于成本价
  104. openSelectedModal: false, // 已选产品弹框是否显示
  105. detailData: null, // 销售单详情数据
  106. editInfo: null, // 添加/编辑 提交的值
  107. }
  108. },
  109. onNavigationBarButtonTap(e){ // 标题栏 按钮操作
  110. this.openModal = true
  111. },
  112. onBackPress() {
  113. uni.$emit("refreshEditBL")
  114. },
  115. onLoad(opt) {
  116. this.theme = getApp().globalData.theme
  117. if(opt){
  118. this.salesInfo = opt.data ? JSON.parse(opt.data) : null
  119. }
  120. this.init()
  121. const _this = this
  122. uni.getSystemInfo({
  123. success: function (res) {
  124. const winH = res.windowHeight * 2
  125. _this.scrollH = winH - 120
  126. }
  127. })
  128. },
  129. methods: {
  130. init(){
  131. this.getList(1) // 产品列表
  132. this.selectedProductTotal() // 已选产品合计
  133. },
  134. // 获取查询参数 刷新列表
  135. refresh(params){
  136. console.log('查询', params)
  137. const _this = this
  138. if(params.vinCode && params.vinCode.length == 17){
  139. // 查询vin码
  140. supperCodeByVin({ vin: params.vinCode }).then(res => {
  141. if(res.status == 200){
  142. params.productCodeList = res.data.partCodeList || []
  143. this.searchParams = params
  144. this.getList(1)
  145. }
  146. })
  147. }else{
  148. this.searchParams = params
  149. this.getList(1)
  150. }
  151. },
  152. // 列表
  153. getList(pageNo){
  154. const _this = this
  155. if (pageNo) {
  156. this.pageNo = pageNo
  157. }
  158. let params = {
  159. pageNo: this.pageNo,
  160. pageSize: this.pageSize
  161. }
  162. let searchParams = JSON.parse(JSON.stringify(this.searchParams))
  163. console.log(searchParams)
  164. if(searchParams){
  165. searchParams.brandName = undefined
  166. searchParams.nowData = undefined
  167. searchParams.warehouseName = undefined
  168. searchParams.cost = undefined
  169. searchParams.cityPrice = undefined
  170. }
  171. this.status = "loading"
  172. querySumByProductLocation(Object.assign(params, searchParams, { customerSn: this.salesInfo.buyerSn, salePriceType: this.salesInfo.priceType, salesBillSn: this.salesInfo.salesBillSn })).then(res => {
  173. if (res.status == 200) {
  174. if(res.data && res.data.list){
  175. res.data.list.map(item => {
  176. item.salesNums = 1
  177. item.currentQty = item.currentQty || 0
  178. item.price = item.lastSalePrice ? item.lastSalePrice : item.salePrice
  179. })
  180. }
  181. if(this.pageNo>1){
  182. this.listData = this.listData.concat(res.data.list || [])
  183. }else{
  184. this.listData = res.data.list || []
  185. }
  186. this.totalNum = res.data.count || 0
  187. } else {
  188. this.listData = []
  189. this.totalNum = 0
  190. this.noDataText = res.message
  191. }
  192. this.status = "loadmore"
  193. })
  194. },
  195. // scroll-view到底部加载更多
  196. onreachBottom() {
  197. if(this.listData.length < this.totalNum){
  198. this.pageNo += 1
  199. this.getList()
  200. }
  201. },
  202. // 添加/编辑产品
  203. handleAdd(data, type){
  204. if(type == 'add'){
  205. this.productInfo = {
  206. productName: data.productName,
  207. productMainImage: data.productMainImage,
  208. productCode: data.productCode,
  209. productOrigCode: data.productOrigCode,
  210. warehouseName: data.warehouseName,
  211. warehouseLocationName: data.warehouseLocationName,
  212. price: data.price,
  213. qty: 1
  214. }
  215. }else if(type == 'edit'){
  216. this.productInfo = {
  217. productName: data.productName,
  218. productMainImage: data.productMainImage,
  219. productCode: data.productCode,
  220. productOrigCode: data.productOrigCode,
  221. warehouseName: data.warehouseEntity && data.warehouseEntity.name,
  222. warehouseLocationName: data.warehouseLocationEntity && data.warehouseLocationEntity.name,
  223. price: data.price,
  224. qty: data.qty
  225. }
  226. }
  227. this.openProductModal = true
  228. this.chooseType = type
  229. this.productData = data
  230. },
  231. // 已选产品 编辑
  232. handleEdit(data){
  233. this.handleAdd(data, 'edit')
  234. },
  235. // 选好了
  236. handleChoose(){
  237. // uni.redirectTo({ url: '/pages/sales/edit?pageType=edit&data='+JSON.stringify(this.detailData) })
  238. uni.$emit("refreshEditBL")
  239. uni.navigateBack()
  240. },
  241. // 查看已选产品
  242. lookProduct(){
  243. this.openSelectedModal = true
  244. },
  245. // 确认产品信息
  246. productConfirm(data){
  247. this.editInfo = data
  248. if(this.chooseType == 'add'){
  249. this.validatePrice() //验证 售价是否低于定价
  250. }else if(this.chooseType == 'edit'){
  251. this.validateStock() //验证 库存是否不足
  252. }
  253. },
  254. // 售价低于定价
  255. validatePrice(){
  256. if (this.productData.lastSalePrice && (this.editInfo.price < this.productData.salePrice)) { // 售价低于定价
  257. this.commonType = 1
  258. this.modalText = '售价低于定价,确认要添加吗?'
  259. this.commonModal = true
  260. }else{
  261. this.validateStock() //验证 库存是否不足
  262. }
  263. },
  264. // 库存不足
  265. validateStock(){
  266. if(this.chooseType == 'add'){ // 添加产品
  267. if (this.productData.currentQty < this.editInfo.qty) {
  268. this.commonType = 2
  269. this.modalText = '库存不足,将产生急件,确认要添加吗?'
  270. this.commonModal = true
  271. }else{
  272. this.validateCost() //验证 售价是否低于成本价
  273. }
  274. }else if(this.chooseType == 'edit'){ // 编辑产品
  275. // 校验库存
  276. stockByProductSn({ productSn: this.productData.productSn }).then(res => {
  277. if (res.status == 200) {
  278. if (res.data && (!res.data.currentStockQty || this.editInfo.qty <= res.data.currentStockQty)) {
  279. this.editProduct()
  280. } else { // 库存不足
  281. this.commonType = 3
  282. this.modalText = '库存不足,将产生急件,确认修改吗?'
  283. this.commonModal = true
  284. }
  285. }
  286. })
  287. }
  288. },
  289. // 售价低于成本价
  290. validateCost(){
  291. if (this.productData.putCost > this.editInfo.price) {
  292. this.commonType = 4
  293. this.modalText = '售价低于成本价,确认要添加吗?'
  294. this.commonModal = true
  295. }else{
  296. this.addProduct()
  297. }
  298. },
  299. // 添加产品
  300. addProduct(){
  301. let params = {
  302. buyerSn: this.detailData.buyerSn,
  303. productSn: this.productData.productSn,
  304. cost: this.productData.putCost,
  305. price: this.editInfo.price,
  306. qty: this.editInfo.qty,
  307. salesBillSn: this.detailData.salesBillSn,
  308. salesBillNo: this.detailData.salesBillNo,
  309. warehouseSn: this.productData.warehouseSn,
  310. warehouseLocationSn: this.productData.warehouseLocationSn,
  311. stockSn: this.productData.stockSn
  312. }
  313. salesDetailInsert(params).then(res => {
  314. if(res.status == 200){
  315. this.toashMsg(res.message)
  316. this.changeList('add', params) // 变更 列表产品可选状态
  317. this.selectedProductTotal() // 已选产品合计
  318. this.commonModal = false
  319. this.openProductModal = false
  320. }
  321. })
  322. },
  323. // 编辑产品
  324. editProduct(){
  325. let params = {
  326. id: this.productData.id,
  327. qty: this.editInfo.qty,
  328. price: this.editInfo.price,
  329. productSn: this.productData.productSn,
  330. salesBillSn: this.detailData.salesBillSn,
  331. }
  332. salesDetailUpdate(params).then(res => {
  333. if(res.status == 200){
  334. this.toashMsg(res.message)
  335. this.$refs.selectedProducts.getList(1)
  336. this.changeList('add', params) // 变更 列表产品可选状态
  337. this.selectedProductTotal() // 已选产品合计
  338. this.commonModal = false
  339. this.openProductModal = false
  340. }
  341. })
  342. },
  343. // 更改产品列表参数
  344. changeList(type, obj){
  345. const _this = this
  346. // 查询已选产品列表中产品编码一致的产品,则为需要变更“已添加”状态的产品
  347. let params = {
  348. pageNo: 1,
  349. pageSize: 100,
  350. salesBillSn: this.detailData.salesBillSn,
  351. productSn: obj.productSn
  352. }
  353. salesDetailList(params).then(res => {
  354. if(res.status == 200){
  355. if(res.data && res.data.list){
  356. let listArr = []
  357. let listInd = []
  358. this.listData.filter(function(item, index){
  359. if(item.productSn == obj.productSn){
  360. listArr.push(item)
  361. listInd.push(index)
  362. }
  363. })
  364. let chooseList = res.data.list || []
  365. if(type == 'add'){
  366. console.log('添加', chooseList,'++',listArr)
  367. listArr.map((subItem, subInd) => {
  368. chooseList.map(item => { // 已选产品 产品sn一致的数据
  369. if(subItem.productSn == item.productSn && subItem.warehouseLocationSn == item.warehouseLocationSn){
  370. _this.$nextTick(() => {
  371. _this.listData[listInd[subInd]].saleQty = 1 // 非0即可,用于判断是否显示添加产品按钮
  372. })
  373. }
  374. })
  375. })
  376. // 添加 产品总库存为0添加为急件时的情况
  377. if(chooseList.length == 1 && chooseList[0].oosFlag==1){
  378. _this.$nextTick(() => {
  379. _this.listData[listInd[0]].saleQty = 1 // 非0即可,用于判断是否显示添加产品按钮
  380. })
  381. }
  382. }else if(type == 'del'){
  383. console.log('删除', chooseList,'++',listArr)
  384. listArr.map((subItem, subInd) => {
  385. _this.$nextTick(() => {
  386. _this.listData[listInd[subInd]].saleQty = 0 // 非0即可,用于判断是否显示添加产品按钮
  387. })
  388. })
  389. listArr.map((subItem, subInd) => {
  390. chooseList.map(item => { // 已选产品 产品sn一致的数据
  391. if(subItem.productSn == item.productSn && subItem.warehouseLocationSn == item.warehouseLocationSn){
  392. _this.$nextTick(() => {
  393. _this.listData[listInd[subInd]].saleQty = 1 // 非0即可,用于判断是否显示添加产品按钮
  394. })
  395. }
  396. })
  397. })
  398. this.selectedProductTotal() // 更新产品款数
  399. }
  400. }
  401. }
  402. })
  403. },
  404. // 删除已选产品
  405. handleDel(data){
  406. this.changeList('del', data) // 变更 列表产品可选状态
  407. },
  408. // 公共弹框 确定
  409. handleCommon(){
  410. if(this.commonType == 1){ // 添加产品时售价低于定价
  411. this.validateStock() //验证 库存是否不足
  412. }else if(this.commonType == 2){ // 添加产品时库存不足
  413. this.validateCost() //验证 售价是否低于成本价
  414. }else if(this.commonType == 3){ // 编辑产品时库存不足
  415. this.editProduct()
  416. }else if(this.commonType == 4){ // 添加产品时售价低于成本价
  417. this.addProduct()
  418. }
  419. },
  420. // 公共弹框 取消
  421. handleCancel(){
  422. this.openProductModal = false
  423. },
  424. // 已选产品合计
  425. selectedProductTotal(){
  426. salesDetail({ id: this.salesInfo.id }).then(res => {
  427. if(res.status == 200){
  428. this.detailData = res.data
  429. }else{
  430. this.detailData = null
  431. }
  432. })
  433. },
  434. }
  435. }
  436. </script>
  437. <style lang="scss">
  438. .chooseProduct-wrap{
  439. width: 100%;
  440. position: relative;
  441. height: 100vh;
  442. overflow: hidden;
  443. .padding_3{
  444. padding: 6upx 0;
  445. }
  446. .color_6{
  447. color: #666;
  448. }
  449. .product-list-box{
  450. padding: 14upx 20upx;
  451. .product-list-con{
  452. .product-list-item{
  453. background: #ffffff;
  454. padding: 20upx 20upx 0;
  455. margin-bottom: 25upx;
  456. border-radius: 25upx;
  457. box-shadow: 1px 1px 3px #EEEEEE;
  458. .list-item-tit{
  459. padding-bottom: 12upx;
  460. border-bottom: 1px solid #e4e7ed;
  461. .item-tit{
  462. font-weight: bold;
  463. color: rgb(48, 49, 51);
  464. font-size: 28upx;
  465. padding-left: 10upx;
  466. border-left: 6upx solid #000;
  467. line-height: 1;
  468. }
  469. }
  470. .list-item-con{
  471. padding: 14upx 0 16upx;
  472. font-size: 26upx;
  473. .pimgs{
  474. margin-right: 16upx;
  475. }
  476. .list-item-btn{
  477. width: 10%;
  478. text-align: center;
  479. }
  480. }
  481. .list-item-footer{
  482. border-top: 1px solid #e4e7ed;
  483. .footer-item{
  484. width: 50%;
  485. text-align: center;
  486. border-right: 1px solid #e4e7ed;
  487. font-size: 26upx;
  488. padding: 8upx 0 12upx;
  489. }
  490. .footer-item:last-child{
  491. border: none;
  492. }
  493. }
  494. }
  495. }
  496. }
  497. .selected-box{
  498. background-color: #fff;
  499. box-shadow: 1px -1px 3px #EEEEEE;
  500. .choose-info{
  501. padding: 0 30upx;
  502. }
  503. .choose-btn{
  504. color: #fff;
  505. width: 28%;
  506. padding: 26upx 0;
  507. text-align: center;
  508. }
  509. }
  510. }
  511. </style>