index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. <template>
  2. <view class="wrap">
  3. <view class="u-menu-wrap">
  4. <scroll-view :style="{height:height}" scroll-y scroll-with-animation class="u-tab-view menu-scroll-view"
  5. :scroll-top="scrollTop" v-if="leftShow">
  6. <view v-for="(item,index) in newTabList" :key="index" class="u-tab-item"
  7. :class="[current==index ? 'u-tab-item-active' : '']" :data-current="index"
  8. @tap.stop="swichMenu(index,item)">
  9. <text class="u-line-1">{{item.categoryName}}</text>
  10. <view class="u-tab-num">
  11. ({{item.num}})
  12. </view>
  13. <u-badge type="error" max="99" :value="item.selectNum" :absolute="true" :offset="[4,4]"></u-badge>
  14. </view>
  15. </scroll-view>
  16. <scroll-view lower-threshold="150" :style="{height:height}" scroll-y class="right-box"
  17. @scrolltolower="lowerBottom">
  18. <view class="page-view">
  19. <view class="class-item" v-if="newCommList.length>0">
  20. <view class="item-container">
  21. <view class="thumb-box" v-for="(item, index) in newCommList" :key="index"
  22. @click.stop="commItemClick(item)">
  23. <view v-if="selectShow">
  24. <image class="select-img"
  25. src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/no_selected.png"
  26. mode="widthFix" v-show="item.noSelect"></image>
  27. <image class="select-img"
  28. src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/selected.png"
  29. mode="widthFix" v-show="!item.noSelect&&item.checked"></image>
  30. <image class="select-img"
  31. src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/select.png"
  32. mode="widthFix" v-show="!item.noSelect&&!item.checked"></image>
  33. </view>
  34. <view class="check-content">
  35. <view class="comm-img">
  36. <u--image radius="4" width="130rpx" height="130rpx" :src="item.cover"
  37. mode="aspectFit" :lazy-lord="true"></u--image>
  38. </view>
  39. <view class="comm-main">
  40. <view>
  41. {{item.name}}
  42. </view>
  43. <view>
  44. 条形码:{{item.barcode}}
  45. </view>
  46. <!-- <view>
  47. 商品类型:{{item.categoryName}}
  48. </view> -->
  49. <view v-if="item.price!=null">
  50. 价格:<text>¥{{item.price}}</text>
  51. </view>
  52. </view>
  53. </view>
  54. </view>
  55. <u-loadmore :status="status" v-if="newCommList.length>=1" />
  56. </view>
  57. </view>
  58. <view class="empty" v-else>
  59. <u-empty></u-empty>
  60. </view>
  61. </view>
  62. </scroll-view>
  63. <view class="cart" v-if="selectShow" @click="cartShow=true">
  64. <image class="cart-img"
  65. src="https://cdn.ossfile.mxrvending.com/assets/xy_merc_mini/images/commodity/buy.png"
  66. mode="widthFix"></image>
  67. <u-badge type="error" max="99" :value="selectList.length" :absolute="true" :offset="[4,4]"></u-badge>
  68. </view>
  69. </view>
  70. <xpopup :show="cartShow" @close="close" :showBtn="false">
  71. <view class="pop-top" slot="title">
  72. <view class="left">
  73. 已选商品
  74. </view>
  75. <xbutton size="mini" class="clear" @click="clearCart">清空</xbutton>
  76. </view>
  77. <view class="pop-content">
  78. <u-list height="440rpx">
  79. <u-list-item v-for="(item, index) in selectList" :key="item.id">
  80. <view class="list-item">
  81. <view class="comm-item">
  82. <view class="comm-img">
  83. <u--image radius="4" width="110rpx" height="110rpx" :src="item.cover"
  84. mode="aspectFit" :lazy-lord="true"></u--image>
  85. </view>
  86. <view class="item-content">
  87. <view>
  88. {{item.name}}
  89. </view>
  90. <view>
  91. 条形码:{{item.barcode}}
  92. </view>
  93. <view>
  94. 商品类型:{{item.categoryName}}
  95. </view>
  96. <view v-if="item.price">
  97. 价格:<text>¥{{item.price}}</text>
  98. </view>
  99. </view>
  100. </view>
  101. <xbutton color="red" size="mini" bgColor="#fff" borderColor="red" @click="delCom(item)">删除
  102. </xbutton>
  103. </view>
  104. </u-list-item>
  105. <view class="empty" v-if="selectList.length==0">
  106. <u-empty mode="car" text="没有商品!"></u-empty>
  107. </view>
  108. </u-list>
  109. </view>
  110. </xpopup>
  111. <xpopup :show="delPopShow" :showBtn="true" @confirm="delSure" @close="delClose">
  112. <view class="del-content">
  113. 是否确定要{{delTitle}}?
  114. </view>
  115. </xpopup>
  116. </view>
  117. </template>
  118. <script>
  119. export default {
  120. data() {
  121. return {
  122. scrollTop: 0, //tab标题的滚动条位置
  123. current: 0, // 预设当前项的值
  124. menuHeight: 0, // 左边菜单的高度
  125. menuItemHeight: 0, // 左边菜单item的高度
  126. keyword: '',
  127. checkedList: [], //当前类目选中商品
  128. cartShow: false, //购物车
  129. delPopShow: false, //删除弹框
  130. delTitle: undefined, //删除或清空提示
  131. selectList: [], //已选中商品
  132. }
  133. },
  134. props: {
  135. value: {
  136. type: Array,
  137. required: false
  138. },
  139. selectShow: {
  140. type: Boolean,
  141. default: false,
  142. require: false
  143. },
  144. leftShow: {
  145. type: Boolean,
  146. default: true,
  147. require: false
  148. },
  149. height: {
  150. type: String,
  151. require: false,
  152. default: 'auto'
  153. },
  154. tabList: {
  155. type: Array,
  156. require: true,
  157. default () {
  158. return []
  159. }
  160. },
  161. commList: {
  162. type: Array,
  163. require: true,
  164. default () {
  165. return []
  166. }
  167. },
  168. status: {
  169. type: String,
  170. require: false,
  171. default: 'loadmore'
  172. },
  173. storeName: {
  174. type: String,
  175. require: false,
  176. default: 'commStor'
  177. },
  178. isModal:{
  179. type: Boolean,
  180. require: false,
  181. default: false
  182. },
  183. },
  184. computed: {
  185. newTabList() {
  186. if (this.tabList.length > 0) {
  187. let newTabList = JSON.parse(JSON.stringify(this.tabList));
  188. if (this.selectShow) {
  189. const idMapping = newTabList.reduce((acc, el, i) => {
  190. el.selectNum = 0;
  191. acc[el.categoryCode] = i;
  192. return acc;
  193. }, {});
  194. this.selectList.forEach(i => {
  195. if (i.categoryCode == null) {
  196. i.categoryCode = '0'
  197. }
  198. newTabList[idMapping[i.categoryCode]].selectNum++
  199. })
  200. return newTabList
  201. } else {
  202. return newTabList
  203. }
  204. } else {
  205. return []
  206. }
  207. },
  208. newCommList() {
  209. let newCommList = [];
  210. if (this.selectList && this.selectList.length > 0) {
  211. let selectList = this.selectList
  212. const idMapping = selectList.reduce((acc, el, i) => {
  213. acc[el.id] = i;
  214. return acc;
  215. }, {});
  216. this.commList.forEach(i => {
  217. if (idMapping[i.id] != undefined) { //重复值
  218. i.checked = true
  219. } else {
  220. i.checked = false
  221. }
  222. newCommList.push(i)
  223. })
  224. } else {
  225. newCommList = this.commList.map(i => {
  226. i.checked = false;
  227. return i
  228. })
  229. }
  230. return newCommList
  231. }
  232. },
  233. watch: {
  234. commList: {
  235. handler(newVal, oldVal) {
  236. },
  237. immediate: true,
  238. deep: true,
  239. },
  240. },
  241. created() {
  242. this.onshow()
  243. },
  244. methods: {
  245. //搜索页来回跳转,刷新已选商品数据
  246. onshow() {
  247. if (uni.getStorageSync(this.storeName) && JSON.parse(uni.getStorageSync(
  248. this.storeName)).length > 0) {
  249. let commStor = JSON.parse(uni.getStorageSync(this.storeName))
  250. this.selectList = commStor;
  251. }
  252. },
  253. // 商品点击
  254. commItemClick(e) {
  255. if (e.noSelect) {
  256. this.$modal.msg('当前商品已存在!')
  257. return
  258. }
  259. if (this.selectShow) { //有选中框
  260. if (this.isModal) { //私库中新建模商品,需要设置价格
  261. if (e.price == null || e.price == undefined) {
  262. this.$emit('comClick', e)
  263. return
  264. }
  265. }
  266. if (!e.checked && this.selectList && this.selectList.length >= 10) {
  267. this.$modal.msg('一次最多添加10个商品!')
  268. return
  269. }
  270. e.checked = !e.checked
  271. //选中商品存储到内存中,以便取用
  272. let commStor = []
  273. if (e.checked) { //选中添加到存储中
  274. if (uni.getStorageSync(this.storeName)) {
  275. commStor = JSON.parse(uni.getStorageSync(this.storeName));
  276. }
  277. commStor.push(e);
  278. } else { //取消选中删除,并且从存储中删除
  279. commStor = JSON.parse(uni.getStorageSync(this.storeName));
  280. for (let i = 0; i < commStor.length; i++) {
  281. let item = commStor[i]
  282. if (item.id == e.id) {
  283. commStor.splice(i, 1);
  284. break
  285. }
  286. }
  287. }
  288. //收集选中商品,更新存储
  289. this.selectList = commStor;
  290. commStor.length > 0 ? uni.setStorageSync(this.storeName, JSON.stringify(commStor)) : uni
  291. .setStorageSync(
  292. this.storeName, '')
  293. if (commStor.length > 0) {
  294. console.log('commStor', commStor)
  295. uni.setStorageSync(this.storeName, JSON.stringify(commStor))
  296. }
  297. } else { //无选中框
  298. this.$emit('comClick', e)
  299. }
  300. },
  301. lowerBottom() {
  302. this.$emit('lowerBottom')
  303. },
  304. // 点击左边的栏目切换
  305. async swichMenu(index, item) {
  306. if (index == this.current) return;
  307. this.current = index;
  308. this.$emit('switchMenu', item)
  309. // 如果为0,意味着尚未初始化
  310. if (this.menuHeight == 0 || this.menuItemHeight == 0) {
  311. await this.getElRect('menu-scroll-view', 'menuHeight');
  312. await this.getElRect('u-tab-item', 'menuItemHeight');
  313. }
  314. // 将菜单菜单活动item垂直居中
  315. this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
  316. },
  317. // 获取一个目标元素的高度
  318. getElRect(elClass, dataVal) {
  319. new Promise((resolve, reject) => {
  320. const query = uni.createSelectorQuery().in(this);
  321. query.select('.' + elClass).fields({
  322. size: true
  323. }, res => {
  324. // 如果节点尚未生成,res值为null,循环调用执行
  325. if (!res) {
  326. setTimeout(() => {
  327. this.getElRect(elClass);
  328. }, 10);
  329. return;
  330. }
  331. this[dataVal] = res.height;
  332. }).exec();
  333. })
  334. },
  335. // 关闭购物车
  336. close() {
  337. this.cartShow = false
  338. },
  339. // 删除当前商品
  340. delCom(item) {
  341. //删除选中商品,更新存储
  342. let commStor = JSON.parse(uni.getStorageSync(this.storeName));
  343. for (let i = 0; i < commStor.length; i++) {
  344. let stor = commStor[i];
  345. if (stor.id == item.id) {
  346. commStor.splice(i, 1);
  347. break
  348. }
  349. }
  350. this.selectList = commStor;
  351. uni.setStorageSync(this.storeName, JSON.stringify(commStor));
  352. this.$modal.msg('删除成功~');
  353. },
  354. //清空购物车
  355. clearCart() {
  356. if (this.selectList.length > 0) {
  357. this.delPopShow = true
  358. this.delTitle = '清空已选商品'
  359. } else {
  360. this.$modal.msg('您没有选择任何商品!');
  361. }
  362. },
  363. delSure() {
  364. this.delPopShow = false
  365. this.selectList = [];
  366. uni.setStorageSync(this.storeName, '')
  367. this.close()
  368. this.$modal.msg('清空成功~');
  369. },
  370. delClose() {
  371. this.delPopShow = false
  372. }
  373. }
  374. }
  375. </script>
  376. <style lang="scss" scoped>
  377. .u-menu-wrap {
  378. flex: 1;
  379. display: flex;
  380. overflow: hidden;
  381. }
  382. .u-tab-view {
  383. width: 200rpx;
  384. height: 100%;
  385. }
  386. .u-tab-item {
  387. height: 110rpx;
  388. background: #f6f6f6;
  389. box-sizing: border-box;
  390. display: flex;
  391. flex-direction: column;
  392. align-items: center;
  393. justify-content: center;
  394. font-size: 26rpx;
  395. color: #444;
  396. font-weight: 400;
  397. line-height: 1;
  398. position: relative;
  399. &::after {
  400. content: '';
  401. height: 1rpx;
  402. width: 100%;
  403. background-color: #fafafa;
  404. position: absolute;
  405. left: 0;
  406. bottom: 0;
  407. }
  408. .u-tab-num {
  409. font-size: 20rpx;
  410. margin-top: 6rpx;
  411. }
  412. }
  413. .u-tab-item-active {
  414. position: relative;
  415. background: #fff;
  416. color: #000;
  417. font-weight: bold;
  418. }
  419. .u-tab-item-active::before {
  420. content: "";
  421. position: absolute;
  422. border-left: 4px solid $uni-color-primary;
  423. height: 32rpx;
  424. left: 0;
  425. top: 39rpx;
  426. }
  427. .u-tab-view {
  428. height: 100%;
  429. }
  430. .right-box {
  431. background-color: rgb(250, 250, 250);
  432. padding-bottom: 20rpx;
  433. position: relative;
  434. }
  435. .page-view {
  436. padding: 16rpx 16rpx 0;
  437. }
  438. .class-item {
  439. background-color: #fff;
  440. padding: 16rpx;
  441. border-radius: 8rpx;
  442. }
  443. .item-title {
  444. font-size: 30rpx;
  445. color: $u-main-color;
  446. font-weight: bold;
  447. margin: 10rpx 0;
  448. }
  449. .item-menu-name {
  450. font-weight: normal;
  451. font-size: 24rpx;
  452. color: $u-main-color;
  453. }
  454. .item-container {
  455. display: flex;
  456. flex-direction: column;
  457. flex-wrap: wrap;
  458. }
  459. .empty {
  460. position: absolute;
  461. left: 50%;
  462. top: 50%;
  463. transform: translate(-50%, -50%);
  464. }
  465. .item-menu-image {
  466. width: 120rpx;
  467. height: 120rpx;
  468. border-radius: 100rpx;
  469. }
  470. .thumb-box {
  471. display: flex;
  472. flex-flow: row nowrap;
  473. padding: 12rpx 0;
  474. align-items: center;
  475. &:not(:last-child) {
  476. border-bottom: 1rpx solid #f4f4f4;
  477. margin-bottom: 10rpx;
  478. }
  479. }
  480. .select-img {
  481. width: 40rpx;
  482. height: 40rpx;
  483. }
  484. .check-content {
  485. display: flex;
  486. flex-direction: row;
  487. align-items: center;
  488. padding-left: 12rpx;
  489. .comm-img {
  490. width: 130rpx;
  491. height: 130rpx;
  492. display: flex;
  493. flex-direction: row;
  494. align-items: center;
  495. justify-content: space-around;
  496. image {
  497. width: 100%;
  498. }
  499. }
  500. .comm-main {
  501. box-sizing: border-box;
  502. padding-left: 18rpx;
  503. color: #999;
  504. >view {
  505. padding: 4rpx 0;
  506. }
  507. >view:nth-child(1) {
  508. font-size: 28rpx;
  509. font-weight: bold;
  510. color: #333;
  511. }
  512. >view:nth-child(2) {
  513. font-size: 24rpx;
  514. max-width: 380rpx;
  515. }
  516. >view:nth-child(3) {
  517. font-size: 24rpx;
  518. }
  519. >view:nth-child(4) {
  520. font-size: 24rpx;
  521. text {
  522. font-weight: bold;
  523. color: red;
  524. }
  525. }
  526. }
  527. }
  528. .cart {
  529. width: 120rpx;
  530. height: 120rpx;
  531. display: flex;
  532. flex-direction: row;
  533. justify-content: center;
  534. align-items: center;
  535. border-radius: 120rpx;
  536. position: fixed;
  537. right: 24rpx;
  538. bottom: 250rpx;
  539. .cart-img {
  540. width: 80rpx;
  541. height: 80rpx;
  542. }
  543. }
  544. .pop-top {
  545. padding: 24rpx;
  546. position: relative;
  547. .left {
  548. font-size: 28rpx;
  549. font-weight: bold;
  550. }
  551. .clear {
  552. position: absolute;
  553. right: 24rpx;
  554. top: 24rpx;
  555. }
  556. }
  557. .pop-content {
  558. // height: 200rpx;
  559. padding: 0 24rpx;
  560. }
  561. .list-item {
  562. display: flex;
  563. flex-flow: row nowrap;
  564. justify-content: space-between;
  565. align-items: center;
  566. width: 100%;
  567. }
  568. .comm-item {
  569. display: flex;
  570. flex-direction: row;
  571. justify-content: flex-start;
  572. align-items: center;
  573. background-color: #fff;
  574. margin-bottom: 12rpx;
  575. border-radius: 12rpx;
  576. box-sizing: border-box;
  577. padding: 12rpx;
  578. .comm-img {
  579. width: 110rpx;
  580. }
  581. .item-content {
  582. width: 480rpx;
  583. padding-left: 24rpx;
  584. color: #999;
  585. >view:nth-child(1) {
  586. font-size: 28rpx;
  587. font-weight: bold;
  588. color: #333;
  589. }
  590. >view:nth-child(2) {
  591. font-size: 24rpx;
  592. margin-top: 12rpx;
  593. }
  594. >view:nth-child(3) {
  595. font-size: 24rpx;
  596. margin-top: 12rpx;
  597. }
  598. >view:nth-child(4) {
  599. font-size: 24rpx;
  600. margin-top: 12rpx;
  601. text {
  602. font-weight: bold;
  603. color: red;
  604. }
  605. }
  606. }
  607. }
  608. .del-content {
  609. padding: 48rpx;
  610. font-size: 32rpx;
  611. text-align: center;
  612. }
  613. </style>