index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. <template>
  2. <block>
  3. <!-- z-paging插件:具体使用文档请看https://z-paging.zxlee.cn/ -->
  4. <z-paging
  5. :auto="false"
  6. :refresher-enabled="false"
  7. auto-show-back-to-top
  8. back-to-top-bottom="300rpx"
  9. :paging-style="{ paddingBottom: 'constant(safe-area-inset-bottom)', paddingBottom: 'env(safe-area-inset-bottom)' }"
  10. ref="paging"
  11. v-model="dataList"
  12. >
  13. <template #top>
  14. <text style="opacity: 0; position: absolute;">{{showEmptyCart}}</text>
  15. <view v-if="!showEmptyCart" style="overflow: hidden" class="l-boxShadow c-bg-f o-plr-24 o-ptb-10">
  16. <view style="float: right">
  17. <u-tag @click="isEdit = !isEdit" :text="isEdit ? '完成' : '编辑'" :type="isEdit ? 'success' : 'warning'"></u-tag>
  18. </view>
  19. </view>
  20. </template>
  21. <view class="o-plr-24 o-pt-20 chgUNumBox">
  22. <u-checkbox-group size="30" v-model="single_checkbox" placement="column" @change="single_checkboxChg">
  23. <view @click.self="isCheckChg(item)" class="l-flex-between l-boxShadow o-mb-20 c-bg-f o-plr-10 o-ptb-10 c-radius-10" v-for="item in dataList" style="border: 1px solid #eee;">
  24. <view class="l-flex-RC">
  25. <view class="l-flex-1"><u-checkbox :checked="item.isCheck" shape="circle" :name="item.imgId"></u-checkbox></view>
  26. <view class="l-flex-3">
  27. <u--image width="250rpx" height="250rpx" mode="aspectFit" :lazy-load="true" :fade="true" duration="450" :src="item.imgUrl">
  28. <view slot="error" style="font-size: 24rpx;">加载失败</view>
  29. </u--image>
  30. </view>
  31. </view>
  32. <view class="l-flex-6">
  33. <view class="c-text-r c-color-8" @click.stop="discountClk(item)">
  34. <u--text align="right" suffixIcon="arrow-right" color="#888" iconStyle="font-size: 18px;color:#888;" text="优惠码"></u--text>
  35. </view>
  36. <view class="u-line-2 o-mtb-30 c-color-8">{{ item.imgName }}</view>
  37. <view class="l-flex-between o-mb-20">
  38. <view class="">
  39. <text class="c-text-16 c-color-MR">¥{{ item.totalPrice }}</text>
  40. </view>
  41. <!-- 如果优惠券总金额大于0,则显示预估到手价 -->
  42. <view v-if="GETT_home_totalCoupon > 0" class="estiPriceCon c-text-12 c-color-f l-flex-RC">
  43. <view class="estiPriceTxt c-text-12 o-ptb-10 o-plr-14">预估到手</view>
  44. <view class="estiPrice c-text-12 o-ptb-10 o-plr-14">¥{{ compEstiPrice(item.totalPrice) }}</view>
  45. </view>
  46. </view>
  47. <u-number-box
  48. @overlimit="
  49. e => {
  50. numsOverlimit(e, item);
  51. }
  52. "
  53. v-model="item.nums"
  54. @change="
  55. e => {
  56. numsChg(e, item);
  57. }
  58. "
  59. disabledInput
  60. @minus="
  61. e => {
  62. goodMinus(e, item);
  63. }
  64. "
  65. @plus="
  66. e => {
  67. goodPlus(e, item);
  68. }
  69. "
  70. ></u-number-box>
  71. </view>
  72. </view>
  73. </u-checkbox-group>
  74. </view>
  75. <template #bottom>
  76. <view v-show="!showEmptyCart" class="l-boxShadow c-bg-f l-flex-between o-plr-24 o-ptb-20" style="width: 100%;">
  77. <view style="flex: 0 0 70px;">
  78. <u-checkbox-group size="30" v-model="all_checkbox" placement="column" @change="all_checkboxChg">
  79. <u-checkbox shape="circle" label="全选" name="全选"></u-checkbox>
  80. </u-checkbox-group>
  81. </view>
  82. <!-- 支付 -->
  83. <view class="l-flex-between o-w" v-if="!isEdit">
  84. <view>
  85. <view class="c-color-1 c-text-14">
  86. 合计:
  87. <text class="c-text-14 c-color-MR">¥{{ priceList.totalPrice }}</text>
  88. </view>
  89. <view class="l-f">
  90. <view class="c-color-1 c-text-14 o-mr-14">
  91. 已优惠:
  92. <text class="c-text-14 c-color-MR">¥{{ priceList.discountPrices }}</text>
  93. </view>
  94. <u--text @click="amountClk" size="14" type="primary" text="查看明细"></u--text>
  95. </view>
  96. </view>
  97. <view>
  98. <u-button
  99. :disabled="totalNums === 0"
  100. v-if="GETT_loginInfos.phone"
  101. @click="toPayPop"
  102. throttleTime="2000"
  103. type="primary"
  104. shape="circle"
  105. size="normal"
  106. :text="`去支付(${totalNums})`"
  107. ></u-button>
  108. <u-button
  109. :disabled="totalNums === 0"
  110. v-else
  111. open-type="getPhoneNumber"
  112. @getphonenumber="getPhone"
  113. type="primary"
  114. shape="circle"
  115. size="normal"
  116. :text="`去支付(${totalNums})`"
  117. ></u-button>
  118. </view>
  119. </view>
  120. <!-- 清除 -->
  121. <view class="" v-else><u-button @click="deleteSelClk" :disabled="totalNums === 0" type="error" shape="circle" :text="`删除(${totalNums})`"></u-button></view>
  122. </view>
  123. <!-- 底部导航栏-占位符解决上拉刷新没显示加载更多的问题 -->
  124. <k-tabbar :fixed="false" :placeholder="false" :safeAreaInsetBottom="false" />
  125. </template>
  126. <!-- 空数据页面显示 -->
  127. <template #empty>
  128. <u-empty :show="showEmptyCart" mode="car" icon="http://cdn.uviewui.com/uview/empty/car.png">
  129. <u-button @click="toHomeIdx" throttleTime="2000" size="small" type="primary" :customStyle="{ marginTop: '10px' }" min="1" text="查看更多商品"></u-button>
  130. </u-empty>
  131. </template>
  132. </z-paging>
  133. <u-modal @confirm="uModalConfirm" @cancel="uModalShow = false" showCancelButton :show="uModalShow" title="提示">
  134. <view class="slot-content"><view class="c-text-c">确定需要删除该商品吗?</view></view>
  135. </u-modal>
  136. <!-- 金额明细弹窗 -->
  137. <k-amountPop ref="kAmountPopRef"></k-amountPop>
  138. <!-- 优惠码弹窗 -->
  139. <k-discountPop ref="kDiscountPopRef"></k-discountPop>
  140. <!-- 支付弹窗 -->
  141. <k-payPop ref="kPayPopRef"></k-payPop>
  142. <!-- 支付提示弹窗 -->
  143. <u-modal @confirm="payShow = false" :show="payShow" title="提示" :content="payContent"></u-modal>
  144. <!-- 优惠券弹窗 -->
  145. <k-coupon ref="kCouponRef"></k-coupon>
  146. </block>
  147. </template>
  148. <script>
  149. // 导入优惠券弹窗
  150. import kCoupon from '@/components/common/k-coupon/index.vue';
  151. // 导入接口
  152. import { API_getPhone,API_setDingYue } from '@/common/api.js';
  153. // 引入辅助函数
  154. import { mapGetters } from 'vuex';
  155. // 导入金额明细弹窗
  156. import kAmountPop from '@/components/common/k-amountPop/index.vue';
  157. // 导入优惠码弹窗
  158. import kDiscountPop from '@/components/common/k-discountPop/index.vue';
  159. // 导入支付弹窗
  160. import kPayPop from '@/components/common/k-payPop/index.vue';
  161. // 引入底部导航栏
  162. import kTabbar from '@/components/common/k-tabbar/k-tabbar.vue';
  163. import cartMixins from '@/mixins/cart.js';
  164. export default {
  165. components: {
  166. kTabbar,
  167. kPayPop,
  168. kDiscountPop,
  169. kAmountPop,
  170. kCoupon
  171. },
  172. data() {
  173. return {
  174. // 是否编辑模式
  175. isEdit: false,
  176. // 支付提示弹窗
  177. payShow: false,
  178. payContent: false,
  179. // 控制模态框的显示
  180. uModalShow: false,
  181. // z-paging插件的主数据,用作列表渲染
  182. dataList: [],
  183. // 全选checkbox的值
  184. all_checkbox: [],
  185. // 单选checkbox的值
  186. single_checkbox: [],
  187. // 用来存储要删除的商品
  188. deleteShops: {}
  189. };
  190. },
  191. mixins: [cartMixins],
  192. computed: {
  193. // 使用辅助函数
  194. ...mapGetters(['GETT_loginInfos', 'GETT_clientId', 'GETT_home_couponList', 'GETT_home_totalCoupon']),
  195. // 购物车数据是否为空
  196. showEmptyCart() {
  197. if (this.$M_IsEmpty(this.$S_tabbar_shopCarts)) return true;
  198. return false;
  199. },
  200. // 单选的选中值
  201. singleCheckbox() {
  202. let single_checkbox = [];
  203. if (this.$S_tabbar_shopCarts.length > 0) {
  204. single_checkbox = this.$S_tabbar_shopCarts.map(item => {
  205. if (item.isCheck) {
  206. return item.imgId;
  207. }
  208. });
  209. }
  210. return single_checkbox;
  211. },
  212. // 全选的选中值
  213. allCheckbox() {
  214. let all_checkbox = [];
  215. if (this.$S_tabbar_shopCarts.length > 0) {
  216. all_checkbox = this.$S_tabbar_shopCarts.map(item => {
  217. if (item.isCheck) {
  218. return item.imgId;
  219. }
  220. });
  221. }
  222. all_checkbox = this.arrTrimSpace(all_checkbox);
  223. if (all_checkbox.length === this.$S_tabbar_shopCarts.length) return ['全选'];
  224. return [];
  225. },
  226. // 选中的总数量
  227. totalNums() {
  228. let totalNums = 0;
  229. if (this.$S_tabbar_shopCarts.length > 0) {
  230. this.$S_tabbar_shopCarts.forEach(item => {
  231. if (item.isCheck) {
  232. totalNums = this.$M_Big(totalNums).add(item.nums);
  233. }
  234. });
  235. }
  236. return totalNums;
  237. },
  238. // 选中的总金额
  239. priceList() {
  240. let totalPrice = 0;
  241. let discountPrices = 0;
  242. if (this.$S_tabbar_shopCarts.length > 0) {
  243. this.$S_tabbar_shopCarts.forEach(item => {
  244. if (item.isCheck) {
  245. let discountPrice = 0;
  246. // 计算出每件商品的总优惠
  247. if (item.discountList && item.discountList.length > 0) {
  248. discountPrice = item.discountList.reduce((prev, cur) => {
  249. return this.$M_Big(prev)
  250. .add(cur)
  251. .toNumber();
  252. }, 0);
  253. }
  254. // 减去总优惠
  255. totalPrice = this.$M_Big(totalPrice)
  256. .add(item.totalPrice)
  257. .sub(discountPrice)
  258. .toNumber();
  259. // 总优惠是
  260. discountPrices = this.$M_Big(discountPrices)
  261. .add(discountPrice)
  262. .toNumber();
  263. }
  264. });
  265. }
  266. return {
  267. totalPrice,
  268. discountPrices
  269. };
  270. },
  271. // 计算预估到手价
  272. compEstiPrice() {
  273. return function(totalPrice) {
  274. const estiPrice = this.$M_Big(totalPrice)
  275. .sub(this.GETT_home_totalCoupon)
  276. .toNumber();
  277. if (estiPrice <= 0) return 0.01;
  278. return estiPrice;
  279. };
  280. }
  281. },
  282. mounted() {
  283. // 首次进入需要调用方法加载
  284. this.$refs.paging.complete(this.$S_tabbar_shopCarts);
  285. },
  286. updated() {
  287. // 当vuex数据更新时重新赋值给购物车
  288. this.dataList = this.$S_tabbar_shopCarts;
  289. this.single_checkbox = this.singleCheckbox;
  290. this.all_checkbox = this.allCheckbox;
  291. },
  292. methods: {
  293. // 点击改变选中状态
  294. isCheckChg(row) {
  295. row.isCheck = !row.isCheck;
  296. },
  297. // 点击删除按钮
  298. deleteSelClk() {
  299. // 如果没有机器编码就提示联系管理员
  300. uni.showModal({
  301. title: '提示',
  302. confirmText: '确定',
  303. confirmColor: '#3c9cff',
  304. content: `确定需要删除选定的商品吗?`,
  305. success: modelRes => {
  306. if (modelRes.confirm) {
  307. let cartList = this.$S_tabbar_shopCarts;
  308. let list = [];
  309. // 筛选出选中的商品
  310. if (cartList.length > 0) {
  311. cartList.forEach((item, index) => {
  312. if (item.isCheck) {
  313. delete cartList[index];
  314. } else {
  315. list.push(item);
  316. }
  317. });
  318. }
  319. // 把数组转成对象
  320. let shopCarts = this.arrAttrToOBj(list, 'imgId');
  321. this.$M_Dp('ACTI_SHOPCARTS', shopCarts);
  322. this.isEdit = false;
  323. }
  324. }
  325. });
  326. },
  327. // 点击查看明细
  328. amountClk() {
  329. this.$refs.kAmountPopRef.popOpen(this.$S_tabbar_shopCarts, this.priceList);
  330. },
  331. // 点击优惠码
  332. discountClk(row) {
  333. this.$refs.kDiscountPopRef.popOpen(row, 1);
  334. },
  335. // 获取手机号
  336. async getPhone(e) {
  337. const { code } = e.detail;
  338. // 如果用户点击允许获取手机号
  339. if (!!code) {
  340. let param = {
  341. code,
  342. id: this.GETT_loginInfos.id
  343. };
  344. // 调用获取手机号的接口
  345. let res = await API_getPhone(param);
  346. // 把手机号信息存到vuex里面
  347. await this.$M_Cm('MUTA_GETOPENID', res);
  348. console.log('GETT_loginInfos', this.GETT_loginInfos);
  349. }
  350. this.toPayPop();
  351. },
  352. // 点击支付按钮
  353. toPayPop() {
  354. // 规定支付的商品数量不能超过5
  355. if (this.totalNums > 3) {
  356. this.payContent = '支付商品的数量不能超过3件,请到购物车页面减少数量后,重新支付!!!';
  357. this.payShow = true;
  358. return;
  359. }
  360. //询问是否订阅取餐通知
  361. this.dingyue();
  362. let list = [];
  363. // 筛选出选中的商品
  364. if (this.$S_tabbar_shopCarts.length > 0) {
  365. this.$S_tabbar_shopCarts.forEach(item => {
  366. if (item.isCheck) {
  367. list.push(item);
  368. }
  369. });
  370. }
  371. // 把数组转成对象
  372. let shopCarts = this.arrAttrToOBj(list, 'imgId');
  373. // 组合要支付的参数
  374. const payParam = {
  375. shopCarts,
  376. clientId: this.GETT_clientId,
  377. id: this.GETT_loginInfos.id
  378. };
  379. // 如果有优惠券要提醒用户去选择优惠券
  380. if (this.GETT_home_couponList.length > 0) {
  381. uni.showModal({
  382. title: '提示',
  383. confirmText: '去使用',
  384. cancelText: '不使用',
  385. confirmColor: '#3c9cff',
  386. content: `当前可使用优惠券${this.GETT_home_couponList.length}张。`,
  387. success: modelRes => {
  388. if (modelRes.confirm) {
  389. // 打开优惠券弹窗
  390. this.$refs.kCouponRef.popOpen(this.GETT_home_couponList, payParam, 2, this.priceList.totalPrice);
  391. } else {
  392. // 显示支付弹窗
  393. this.$refs.kPayPopRef.popOpen(payParam, 2);
  394. }
  395. }
  396. });
  397. } else {
  398. // 显示支付弹窗
  399. this.$refs.kPayPopRef.popOpen(payParam, 2);
  400. }
  401. },
  402. //询问是否订阅取餐通知
  403. dingyue(){
  404. var that = this;
  405. wx.requestSubscribeMessage({
  406. tmplIds: ['NVmdaK4MwygT63ME830pwM6wZt4eufxhBr6jlQ2XXJ4'],
  407. success(res) {
  408. var status = res.NVmdaK4MwygT63ME830pwM6wZt4eufxhBr6jlQ2XXJ4;
  409. if(status=='accept'){
  410. //通知后台该用户订阅了消息
  411. var id = that.GETT_loginInfos.id;
  412. API_setDingYue( {id:id} ).then(res => {
  413. console.log('res', res);
  414. });
  415. }
  416. console.log('授权成功');
  417. }
  418. })
  419. },
  420. // 处理购物车数据
  421. handleCar(e, row, idx) {
  422. let shopCarts = this.$S_tabbar_shopCarts;
  423. if (shopCarts.length > 0) {
  424. shopCarts.forEach(item => {
  425. if (item.imgId === row.imgId) {
  426. switch (idx) {
  427. // 给优惠码和优惠价的列表添加一个0值
  428. case 1:
  429. item.codeList.push(0);
  430. item.discountList.push(0);
  431. break;
  432. // 去掉优惠码和优惠价的列表的最后一个
  433. case 2:
  434. item.codeList.pop();
  435. item.discountList.pop();
  436. break;
  437. // 找到购物车数据对应商品的,重新计算总价格
  438. case 3:
  439. item.nums = e.value;
  440. item.totalPrice = this.$M_Big(item.goodsPrice)
  441. .mul(item.nums)
  442. .toNumber();
  443. break;
  444. }
  445. }
  446. });
  447. }
  448. // 把数组转成对象储存到vuex中
  449. let cartList = this.arrAttrToOBj(shopCarts, 'imgId');
  450. this.$M_Dp('ACTI_SHOPCARTS', cartList);
  451. },
  452. // 根据数组的某个属性转成对象
  453. arrAttrToOBj(arr, attr, obj = {}) {
  454. if (arr.length > 0) {
  455. arr.forEach(item => {
  456. obj[item[attr]] = item;
  457. });
  458. return obj;
  459. } else {
  460. return {};
  461. }
  462. },
  463. // 去掉数组的空格,null,undefeated类型
  464. arrTrimSpace(array) {
  465. let list = [];
  466. array.forEach(item => {
  467. if (item == '' || item == null || typeof item == 'undefined') {
  468. } else {
  469. list.push(item);
  470. }
  471. });
  472. return list;
  473. },
  474. // 点击单选按钮
  475. single_checkboxChg(e) {
  476. let shopCarts = this.$S_tabbar_shopCarts;
  477. let singleList = this.arrTrimSpace(e);
  478. // 如果存在单选
  479. if (singleList.length > 0) {
  480. if (shopCarts.length > 0) {
  481. shopCarts.forEach(item => {
  482. item.isCheck = false;
  483. singleList.forEach(item1 => {
  484. if (item.imgId === item1) {
  485. item.isCheck = true;
  486. }
  487. });
  488. });
  489. }
  490. } else {
  491. if (shopCarts.length > 0) {
  492. // 取消全选,具体是遍历数组,把全选的isCheck改为false
  493. shopCarts.forEach(item => {
  494. item.isCheck = false;
  495. });
  496. }
  497. }
  498. // 把数组转成对象储存到vuex中
  499. let cartList = this.arrAttrToOBj(shopCarts, 'imgId');
  500. this.$M_Dp('ACTI_SHOPCARTS', cartList);
  501. },
  502. // 点击全选按钮
  503. all_checkboxChg(e) {
  504. let shopCarts = this.$S_tabbar_shopCarts;
  505. let all_checkbox = [];
  506. // 全选的长度大于0
  507. if (e.length > 0) {
  508. if (shopCarts.length > 0) {
  509. // 全选,具体是遍历数组,把全选的isCheck改为true,并且把控制全选的checkbox赋值
  510. shopCarts.forEach(item => {
  511. item.isCheck = true;
  512. all_checkbox.push(item.imgId);
  513. });
  514. }
  515. } else {
  516. // 取消全选,具体是遍历数组,把全选的isCheck改为false
  517. if (shopCarts.length > 0) {
  518. shopCarts.forEach(item => {
  519. item.isCheck = false;
  520. });
  521. }
  522. }
  523. // 全选的值
  524. this.all_checkbox = all_checkbox;
  525. // 把数组转成对象储存到vuex中
  526. let cartList = this.arrAttrToOBj(shopCarts, 'imgId');
  527. this.$M_Dp('ACTI_SHOPCARTS', cartList);
  528. },
  529. // 点击去首页
  530. toHomeIdx() {
  531. // 改变vuex的数据
  532. this.$M_Dp('ACTI_TABIDX', 0);
  533. }
  534. }
  535. };
  536. </script>
  537. <style lang="scss" scoped>
  538. .estiPriceCon {
  539. .estiPriceTxt {
  540. position: relative;
  541. color: #ff9900;
  542. background-color: #fff5f4;
  543. border-right: 1px solid #fff;
  544. }
  545. .estiPrice {
  546. background-color: #f15c34;
  547. border-top-right-radius: 10rpx;
  548. border-bottom-right-radius: 10rpx;
  549. }
  550. }
  551. </style>