leftArea.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. <template>
  2. <view class="itemList-box">
  3. <view class="title">
  4. 列表
  5. <text style="float:right;">{{ getMyShopInfo.shopName=='首店'? getMyShopInfo.merchantName: getMyShopInfo.merchantName+' '+getMyShopInfo.shopName}}</text>
  6. </view>
  7. <scroll-view
  8. class="itemList-item_box"
  9. scroll-y
  10. scroll-with-animation
  11. :scroll-into-view="activeScrollIntoView"
  12. >
  13. <view
  14. v-for="item in selectList"
  15. :key="getItemKey(item)"
  16. :id="getItemScrollId(item)"
  17. :data-item-key="getItemKey(item)"
  18. class="show-item_box"
  19. @touchstart="onItemTouchStartByEvent"
  20. @touchmove="onItemTouchMoveByEvent"
  21. @touchend="onItemTouchEndByEvent"
  22. @touchcancel="onItemTouchEndByEvent"
  23. >
  24. <view class="delete-btn" :data-item-key="getItemKey(item)" @click.stop="delItemByEvent">删除</view>
  25. <view
  26. class="item-body_box not-active"
  27. :data-item-key="getItemKey(item)"
  28. :class="{'is-swiped': swipeItemId === item.id, 'is-operated': activeItemKey === getItemKey(item)}"
  29. @click="handleItemClickByEvent"
  30. >
  31. <view class="flower-name_box">
  32. <view class="current-item-title">
  33. <text class="name">{{item.name}}</text>
  34. <view class="num-action_box">
  35. <view class="num-btn" :data-item-key="getItemKey(item)" @click.stop="changeItemNumByEvent($event, -1)">
  36. <zui-svg-icon icon="general-sub" :width="38" :height="38" color="#8abf8a" />
  37. </view>
  38. <view class="num-btn" :data-item-key="getItemKey(item)" @click.stop="changeItemNumByEvent($event, 1)">
  39. <zui-svg-icon icon="general-plus" :width="38" :height="38" color="#8abf8a" />
  40. </view>
  41. </view>
  42. </view>
  43. </view>
  44. <view class="item-price-num">
  45. <template v-if="item.property && item.property == 1">
  46. <view>{{parseFloat(item.unitPrice)}}元/{{item.unitName}}</view>
  47. <view>{{item.currentNum}}{{item.unitName}}</view>
  48. </template>
  49. <template v-else>
  50. <view>{{parseFloat(item.unitPrice)}}元/份</view>
  51. <view>{{item.currentNum}}份</view>
  52. </template>
  53. <view>¥{{parseFloat((item.currentNum * item.unitPrice).toFixed(2))}}</view>
  54. </view>
  55. </view>
  56. </view>
  57. </scroll-view>
  58. <view class="itemList-footer_box">
  59. <view> 数量
  60. <text style="margin-left:5upx;">
  61. <text v-if="Number(allCount.bigNum)>0">{{allCount.bigNum}}扎</text><text v-if="Number(allCount.smallNum)>0">{{allCount.smallNum}}支</text>
  62. </text>
  63. </view>
  64. <view> 合计 <text style="margin-left:5upx;">¥{{allPrice}}</text> </view>
  65. </view>
  66. <!-- 开单选花材输入数量和金额 -->
  67. <uni-popup ref="selectNumPriceRef" background-color="#fff" type="center" :animation="false">
  68. <selectNumPrice @confirmAddItemModel="confirmAddItemModel" :customData.sync="customData" @cancelKdSelectNumPrice="addItemModelHidden"></selectNumPrice>
  69. </uni-popup>
  70. <!-- 花材网格选择弹框 -->
  71. <uni-popup ref="selectFlowerGridRef" background-color="#fff" type="center" :animation="false">
  72. <selectFlowerGrid :flowerData="globalGridFlowerList" @globalClearXj="globalClearXj" @globalConfirmChangeXj="globalConfirmChangeXj" @globalCloseXjPop="globalCloseXjPop" :customData.sync="customData"></selectFlowerGrid>
  73. </uni-popup>
  74. </view>
  75. </template>
  76. <script>
  77. import productMins from "@/mixins/product";
  78. import selectNumPrice from './selectNumPrice.vue'
  79. import { mapGetters } from "vuex";
  80. import selectFlowerGrid from './selectFlowerGrid.vue'
  81. export default {
  82. name:'leftItem',
  83. components:{selectNumPrice,selectFlowerGrid},
  84. mixins: [productMins],
  85. props: {
  86. changeCurrentJobType:{
  87. type: String,
  88. default: 'bill'
  89. },
  90. changeCurrentJobId:{
  91. type: Number,
  92. default: 0
  93. },
  94. activeItemMark:{
  95. type: Object,
  96. default(){
  97. return { key:'', time:0 }
  98. }
  99. }
  100. },
  101. data(){
  102. return {
  103. totalShop:{ bigUnit: 0, smallUnit:0, totalPrice: 0 },
  104. itemList:[],
  105. swipeItemId: '',
  106. activeItemKey: '',
  107. activeScrollIntoView: '',
  108. touchStartX: 0,
  109. touchStartY: 0,
  110. isHorizontalSwipe: false,
  111. lastSwipeTime: 0
  112. }
  113. },
  114. computed:{
  115. ...mapGetters(["getMyShopInfo"]),
  116. },
  117. watch:{
  118. changeCurrentJobType:{
  119. handler(id){
  120. this.currentJobType = id
  121. },
  122. immediate:true
  123. },
  124. changeCurrentJobId:{
  125. handler(id){
  126. this.currentJobId = id
  127. },
  128. immediate:true
  129. },
  130. activeItemMark:{
  131. handler(mark){
  132. if(mark && mark.key){
  133. this.activeItemKey = mark.key
  134. this.scrollToActiveItemByKey(mark.key)
  135. }
  136. },
  137. immediate:true
  138. },
  139. selectList:{
  140. handler(){
  141. if(this.activeItemKey){
  142. this.scrollToActiveItemByKey(this.activeItemKey)
  143. }
  144. },
  145. deep:true
  146. }
  147. },
  148. methods:{
  149. getTouchPoint(event) {
  150. const touches = event.changedTouches || event.touches || []
  151. return touches[0]
  152. },
  153. getItemKey(item) {
  154. const property = item.property !== undefined ? item.property : 1
  155. return `${item.id}_${property}`
  156. },
  157. getScrollIdByKey(key) {
  158. return `select-item-${key}`
  159. },
  160. getItemScrollId(item) {
  161. return this.getScrollIdByKey(this.getItemKey(item))
  162. },
  163. getEventItem(event) {
  164. const dataset = event && event.currentTarget && event.currentTarget.dataset
  165. const key = dataset && dataset.itemKey
  166. if (!key) return null
  167. return this.selectList.find(item => this.getItemKey(item) === key)
  168. },
  169. scrollToActiveItemByKey(key) {
  170. this.activeScrollIntoView = ''
  171. this.$nextTick(() => {
  172. this.activeScrollIntoView = this.getScrollIdByKey(key)
  173. })
  174. },
  175. setActiveItem(item) {
  176. const key = this.getItemKey(item)
  177. this.activeItemKey = key
  178. this.scrollToActiveItemByKey(key)
  179. },
  180. clearActiveItem(item) {
  181. if (this.activeItemKey === this.getItemKey(item)) {
  182. this.activeItemKey = ''
  183. this.activeScrollIntoView = ''
  184. }
  185. },
  186. hasEnoughStock(item, num) {
  187. if (!this.checkStock || num <= 0) return true
  188. if (item.unitType == 0) {
  189. return Number(num) <= Number(item.stock)
  190. }
  191. const stockNum = (Number(num) / Number(item.ratio)).toFixed(2)
  192. return Number(stockNum) <= Number(item.stock)
  193. },
  194. showNoStockTip(item) {
  195. this.$util.noStockRemind()
  196. this.$msg(item.unitType == 0 ? "库存不足呢" : "库存不足哦")
  197. },
  198. onItemTouchStart(item, event) {
  199. if (!item) return
  200. const touch = this.getTouchPoint(event)
  201. if (!touch) return
  202. this.touchStartX = touch.clientX
  203. this.touchStartY = touch.clientY
  204. this.isHorizontalSwipe = false
  205. },
  206. onItemTouchStartByEvent(event) {
  207. this.onItemTouchStart(this.getEventItem(event), event)
  208. },
  209. onItemTouchMove(item, event) {
  210. if (!item) return
  211. const touch = this.getTouchPoint(event)
  212. if (!touch) return
  213. const moveX = touch.clientX - this.touchStartX
  214. const moveY = touch.clientY - this.touchStartY
  215. if (Math.abs(moveX) <= Math.abs(moveY) || Math.abs(moveX) < 15) return
  216. this.isHorizontalSwipe = true
  217. this.swipeItemId = moveX < 0 ? item.id : ''
  218. },
  219. onItemTouchMoveByEvent(event) {
  220. this.onItemTouchMove(this.getEventItem(event), event)
  221. },
  222. onItemTouchEnd(item) {
  223. if (!item) return
  224. if (this.isHorizontalSwipe) {
  225. this.lastSwipeTime = Date.now()
  226. }
  227. this.touchStartX = 0
  228. this.touchStartY = 0
  229. this.isHorizontalSwipe = false
  230. },
  231. onItemTouchEndByEvent(event) {
  232. this.onItemTouchEnd(this.getEventItem(event))
  233. },
  234. handleItemClick(item) {
  235. if (!item) return
  236. if (this.swipeItemId === item.id) {
  237. if (Date.now() - this.lastSwipeTime < 300) return
  238. this.swipeItemId = ''
  239. return
  240. }
  241. this.popAddModelFn(item)
  242. },
  243. handleItemClickByEvent(event) {
  244. this.handleItemClick(this.getEventItem(event))
  245. },
  246. popAddModelFn(info) {
  247. this.setActiveItem(info)
  248. let currentNum = info.currentNum
  249. let unitPrice = info.unitPrice
  250. let unitType = info.unitType
  251. this.$util.hitVoice()
  252. this.showAddModelFn({ ...info, currentNum: currentNum, unitPrice: unitPrice,unitType:unitType,action:'update'})
  253. },
  254. delItem(item){
  255. if (!item) return
  256. this.$util.hitVoice()
  257. this.swipeItemId = ''
  258. this.clearActiveItem(item)
  259. item.currentNum = 0
  260. this.replaceItemFn(item)
  261. },
  262. delItemByEvent(event) {
  263. this.delItem(this.getEventItem(event))
  264. },
  265. changeItemNum(item, step) {
  266. if (!item) return
  267. this.$util.hitVoice()
  268. this.swipeItemId = ''
  269. const currentNum = Number(item.currentNum) || 0
  270. const nextNum = Math.max(0, currentNum + step)
  271. if (!this.hasEnoughStock(item, nextNum)) {
  272. this.showNoStockTip(item)
  273. return
  274. }
  275. item.currentNum = nextNum
  276. if (nextNum > 0) {
  277. this.setActiveItem(item)
  278. } else {
  279. this.clearActiveItem(item)
  280. }
  281. this.replaceItemFn(item)
  282. },
  283. changeItemNumByEvent(event, step) {
  284. this.changeItemNum(this.getEventItem(event), step)
  285. }
  286. }
  287. }
  288. </script>
  289. <style lang="scss" scoped>
  290. .itemList-box {
  291. height: 100%;
  292. display: flex;
  293. flex-direction: column;
  294. & .title {
  295. padding: 3upx;
  296. font-size: 12upx;
  297. background-color: #3385FF;
  298. color: #FFFFFF;
  299. }
  300. & .itemList-item_box {
  301. margin-bottom:72upx;
  302. overflow-y: auto;
  303. flex: 1;
  304. height: 0;
  305. border: 1px solid #eee;
  306. background-color: #f3f6f4;
  307. & .show-item_box {
  308. padding: 3upx 4upx;
  309. position: relative;
  310. overflow: hidden;
  311. .delete-btn {
  312. position: absolute;
  313. top: 4upx;
  314. right: 5upx;
  315. bottom: 4upx;
  316. width: 40upx;
  317. display: flex;
  318. align-items: center;
  319. justify-content: center;
  320. color: #FFFFFF;
  321. font-size: 12upx;
  322. background-color: #f56c6c;
  323. border-radius: 4upx;
  324. }
  325. & .item-body_box {
  326. position: relative;
  327. border-radius: 4upx;
  328. padding: 2upx;
  329. font-size: 14upx;
  330. color:#666666;
  331. border-bottom: 3upx solid transparent;
  332. transition: transform 0.2s;
  333. z-index: 1;
  334. box-shadow: 0 2upx 8upx rgba(0, 0, 0, 0.08);
  335. &.is-swiped {
  336. transform: translateX(-42upx);
  337. }
  338. &.is-operated {
  339. border-bottom-color: #3385FF;
  340. }
  341. &.active {
  342. background-color: #ffffff;
  343. }
  344. &.not-active {
  345. background-color: #ffffff;
  346. }
  347. .item-price-num{
  348. color:green;
  349. font-size:11upx;
  350. font-weight:bold;
  351. }
  352. & .flower-name_box {
  353. padding: 0 5upx;
  354. .current-item-title {
  355. display: flex;
  356. align-items: center;
  357. justify-content: space-between;
  358. .name{
  359. color: #333333;
  360. font-weight: bold;
  361. overflow: hidden;
  362. text-overflow:ellipsis;
  363. white-space: nowrap;
  364. width:90upx;
  365. font-size:11upx;
  366. }
  367. .num-action_box {
  368. display: flex;
  369. align-items: center;
  370. margin-top: 2upx;
  371. .num-btn {
  372. width: 20upx;
  373. height: 20upx;
  374. display: flex;
  375. align-items: center;
  376. justify-content: center;
  377. margin-left: 12upx;
  378. }
  379. }
  380. }
  381. }
  382. & > view:nth-child(2) {
  383. display: flex;
  384. align-items: center;
  385. justify-content: space-between;
  386. padding-top: 15upx;
  387. }
  388. }
  389. }
  390. }
  391. & .itemList-footer_box {
  392. height: 20upx;
  393. border: 1px solid #eee;
  394. font-size: 10upx;
  395. display: flex;
  396. align-items: center;
  397. justify-content: space-around;
  398. }
  399. }
  400. </style>