chooseProduct.vue 17 KB

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