mallPage.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. <template>
  2. <view class="ezy-mall-page">
  3. <view class="icon-title-navBar-box">
  4. <view @click="goBack" class="nav-bar-icon"></view>
  5. <text class="nav-bar-title">鹅状元进阶营</text>
  6. </view>
  7. <view class="ezy-tab-border">
  8. <uni-segmented-control :current="productData.current" :values="productData.items" active-color="#3A7FE9"
  9. @clickItem="onChangeTab" class="ezy-tab-box" />
  10. <view>
  11. <view v-if="productData.current === 0">
  12. <scroll-view scroll-y="true" :scroll-into-view="scrollIntoId" class="order-scroll-view">
  13. <!--数学-->
  14. <view :id="'item-' + item.id" v-for="item in productData.list" :key="item.id"
  15. class="mall-list-item">
  16. <view class="mall-content-box">
  17. <img :src='item.cover' class="mall-image" />
  18. <view class="content-body-box">
  19. <view class="content-name">
  20. <view class="name-text">{{item.name}}</view>
  21. </view>
  22. <view class="content-text">{{item.intro}}</view>
  23. <view class="content-row">
  24. <view class="content-yuanjia">原价:{{item.yuanjia}}</view>
  25. <view class="shop-car-box" @click="addProduct(item)">
  26. <icon v-if="productData.selectedIds.includes(item.id)" class="car-change">
  27. </icon>
  28. </view>
  29. </view>
  30. </view>
  31. </view>
  32. <view class="mall-bottom-row">
  33. <view v-if="item.type ==1" class="kcb-btn" @click="productBtn">课程包明细<icon>
  34. </icon>
  35. </view>
  36. <view class="hdj-text">活动价:{{item.xianjia}}</view>
  37. </view>
  38. </view>
  39. </scroll-view>
  40. </view>
  41. <view v-if="productData.current === 1">
  42. <view>英语</view>
  43. </view>
  44. <!-- <view v-if="productData.current === 2">
  45. <view>语文</view>
  46. </view> -->
  47. </view>
  48. </view>
  49. <view class="footer-mall-pay-box">
  50. <view class="mall-left-box" @click="detailBtn">
  51. <!-- 需要换成从接口中取得 wgy -->
  52. <view class="price-icon-box">
  53. <text class="red-price fh-text">¥</text>
  54. <text class="red-price">{{totalPrice}}</text>明细
  55. <icon :class="mxjtClass"></icon>
  56. </view>
  57. <view>购买即同意虚拟产品不支持退订</view>
  58. </view>
  59. <!-- 微信 -->
  60. <view class="pay-status-box" v-if="payType =='weixin'&&currentPlatform =='android'" @click="switchPayWay">
  61. <icon class="wx-icon"></icon>微信
  62. </view>
  63. <view class="pay-status-box" v-if="payType =='zhifubao'&&currentPlatform =='android'" @click="switchPayWay">
  64. <icon class="zfb-icon"></icon>支付宝
  65. </view>
  66. <view class="pay-status-box apple-status-box" v-if="currentPlatform =='ios'">
  67. <icon class="apple-icon"></icon>apple
  68. </view>
  69. <view v-if="currentPlatform =='android'&&!disabled" class="open-svip-btn" @touchstart="creatOrder">立即支付
  70. </view>
  71. <view v-if="currentPlatform =='android'&&disabled" class="open-svip-btn-hui">立即支付</view>
  72. <view v-if="currentPlatform =='ios'&&!disabled" style="margin-top: 20rpx;" class="open-svip-btn"
  73. @touchstart="creatOrderIos">立即支付</view>
  74. <view v-if="currentPlatform =='ios'&&disabled" style="margin-top: 20rpx;" class="open-svip-btn-hui">立即支付
  75. </view>
  76. </view>
  77. <detail-dialog ref="mallDetailPopup" :selected-list="getSelectedProducts"></detail-dialog>
  78. <!--
  79. <product-dialog ref="mallProductPopup" @payBtn="payBtn"></product-dialog> -->
  80. </view>
  81. </template>
  82. <script setup>
  83. import {
  84. reactive,
  85. computed,
  86. nextTick,
  87. ref
  88. } from "vue";
  89. import {
  90. getMallist
  91. } from "@/api/productMall.js";
  92. import {
  93. orderAdd,
  94. orderPayAli,
  95. orderPayWx,
  96. orderPayApple,
  97. orderCheck
  98. } from "@/api/order.js"
  99. import {
  100. onLoad
  101. } from "@dcloudio/uni-app";
  102. import {
  103. toast,
  104. debounce,
  105. getUserIdentity
  106. } from "@/utils/common";
  107. import cacheManager from '@/utils/cacheManager.js';
  108. import CustomTabBar from '@/components/custom-tabbar/custom-tabbar.vue';
  109. import detailDialog from './detailDialog.vue'
  110. import productDialog from './productDialog.vue'
  111. const showPayWay = ref(true)
  112. const showDetail = ref(false)
  113. const mxjtClass = ref('mxjt-sq-icon')
  114. const mallDetailPopup = ref(null);
  115. const mallProductPopup = ref(null);
  116. let scrollIntoId = ref(null)
  117. const productData = reactive({
  118. items: ['数学', '英语'],
  119. current: 1,
  120. from: '',
  121. cardId: '',
  122. list: [],
  123. selectedIds: [] // 新增选中状态存储
  124. })
  125. let currentPlatform = ref('android')
  126. let disabled = ref(false)
  127. const payType = ref('weixin');
  128. let orderId = ref('');
  129. let productId = ref(null)
  130. let iapChannel = ref(null)
  131. let quantity = ref(1)
  132. let channel = ref('')
  133. let appleFlag = ref('')
  134. const totalPrice = computed(() => {
  135. return productData.list
  136. .filter(item => productData.selectedIds.includes(item.id))
  137. .reduce((sum, item) => sum + parseFloat(item.xianjia || 0), 0)
  138. .toFixed(2)
  139. })
  140. const getSelectedProducts = computed(() => {
  141. return productData.list.filter(item => productData.selectedIds.includes(item.id));
  142. });
  143. function switchPayWay() {
  144. payType.value = payType.value == 'weixin' ? 'zhifubao' : 'weixin'
  145. }
  146. function productBtn() {
  147. mallProductPopup.value.detailShow();
  148. }
  149. function detailBtn() {
  150. showDetail.value = !showDetail.value;
  151. if (showDetail.value) {
  152. mxjtClass.value = 'mxjt-zk-icon';
  153. mallDetailPopup.value.detailShow();
  154. } else {
  155. mxjtClass.value = 'mxjt-sq-icon';
  156. mallDetailPopup.value.detailCloseBtn();
  157. }
  158. }
  159. const isIOSorAndroid = () => {
  160. const systemInfo = uni.getSystemInfoSync();
  161. console.log('systemInfo', systemInfo);
  162. if (systemInfo.platform == 'ios') {
  163. return currentPlatform.value = 'ios'
  164. } else {
  165. return currentPlatform.value = 'android'
  166. }
  167. }
  168. function genggaiVip(data) {
  169. uni.hideLoading();
  170. disabled.value = false
  171. const localList = cacheManager.get('auth').levelIdList || []
  172. const mergeList = [...new Set([...localList, ...data.levelIdList])]
  173. cacheManager.updateObject('auth', {
  174. levelIdList: mergeList
  175. })
  176. toast("chenggong!!!! 之后跳转我的订单页面")
  177. // if (formPage.value == 'my') {
  178. // uni.redirectTo({
  179. // url: '/pages/my/index'
  180. // })
  181. // } else {
  182. // uni.redirectTo({
  183. // url: '/pages/study/index'
  184. // })
  185. // }
  186. }
  187. const creatOrderIos = debounce((data => {
  188. disabled.value = true
  189. uni.showLoading({
  190. title: '',
  191. mask: true
  192. });
  193. if (appleFlag.value.toString() == 'true') {
  194. productId.value = 'llisoftEzhuangyuanceshi'
  195. } else {
  196. if (cardId.value == 1) {
  197. productId.value = 'llisoftEzhuangyuan'
  198. } else {
  199. productId.value = 'llisoftEzhuangyuanYingyu'
  200. }
  201. }
  202. if (orderId.value) {
  203. uni.hideLoading();
  204. applePay()
  205. } else {
  206. let req = {
  207. cardId: cardId.value
  208. }
  209. orderAdd(req).then(res => {
  210. console.log('res', res);
  211. if (res.code == 0) {
  212. uni.hideLoading();
  213. orderId.value = res.data.id
  214. // 测试ios 1元
  215. applePay()
  216. } else {
  217. uni.hideLoading();
  218. //disabled.value = false
  219. return false
  220. console.log('请求失败');
  221. }
  222. }).catch((e) => {
  223. uni.hideLoading();
  224. disabled.value = false
  225. toast("订单创建失败")
  226. return false
  227. })
  228. }
  229. }), 500)
  230. function applePaySuccess(data) {
  231. uni.showLoading({
  232. title: '会员开通中',
  233. mask: true
  234. });
  235. let req = {
  236. "id": orderId.value,
  237. "paynum": data.transactionIdentifier,
  238. "receipt": data.transactionReceipt
  239. }
  240. console.log('reqreq', req);
  241. orderPayApple(req).then(res => {
  242. if (res.code == 0 && res.data) {
  243. iapChannel.finishTransaction(data.transactionIdentifier)
  244. console.log('resiapChanneliapChanneliapChannel', res);
  245. genggaiVip()
  246. } else {
  247. //disabled.value = false
  248. iapChannel.finishTransaction(data.transactionIdentifier)
  249. uni.hideLoading();
  250. toast("苹果内购失败")
  251. console.log('orderPayApple失败');
  252. return false
  253. }
  254. })
  255. }
  256. function applePay() {
  257. console.log('123123');
  258. if (!productId.value) {
  259. uni.showToast({
  260. title: '苹果内购ID缺失,请选择其它支付方式或联系客服',
  261. icon: "none"
  262. });
  263. return false;
  264. }
  265. uni.showLoading({
  266. title: '正在支付中...'
  267. });
  268. try {
  269. plus.payment.getChannels(function(channels) { //判读项目支付通道开通情况
  270. for (var i in channels) {
  271. iapChannel = channels[i];
  272. // 获取 id 为 'appleiap' 的 channel
  273. console.info("支付通道", iapChannel)
  274. if (iapChannel.id === 'appleiap') { //开通了app应用内支付,在manifest.josn中设置,开通后需打自定议基座
  275. console.info("苹果支付通道", iapChannel)
  276. // ids 数组中的项为 App Store Connect 配置的内购买项目产品ID(productId)
  277. var ids = [productId.value];
  278. // iap 为应用内支付对象
  279. iapChannel.requestOrder(ids, function(e) {
  280. // 获取订单信息成功回调方法
  281. console.log('requestOrder success: ' + JSON.stringify(e));
  282. uni.requestPayment({
  283. provider: 'appleiap',
  284. orderInfo: {
  285. productid: productId.value, //产品id,来自于苹果
  286. quantity: quantity.value, //产品数量
  287. manualFinishTransaction: true
  288. },
  289. success: (e) => {
  290. uni.hideLoading();
  291. // toast("苹果内购成功")
  292. console.info("苹果内购成功", e)
  293. applePaySuccess(e)
  294. //e.payment.orderNo = that.orderNo
  295. //支付成功回调,前端调用后台接口
  296. },
  297. fail: (e) => {
  298. // disabled.value = false
  299. uni.hideLoading();
  300. toast("苹果内购失败")
  301. console.info("苹果内购失败", e)
  302. },
  303. })
  304. },
  305. function(e) {
  306. // 获取订单信息失败回调方法
  307. console.log('requestOrder failed: ' + JSON.stringify(e));
  308. });
  309. } else {
  310. console.log('不支持苹果支付')
  311. }
  312. }
  313. },
  314. function(e) {
  315. console.log("获取iap支付通道失败:" + e.message);
  316. });
  317. } catch (e) {
  318. uni.showModal({
  319. title: "init",
  320. content: e.message,
  321. showCancel: false
  322. });
  323. // disabled.value = false
  324. } finally {
  325. uni.hideLoading();
  326. }
  327. }
  328. function wxPay() {
  329. orderPayWx({
  330. id: orderId.value
  331. }).then(res2 => {
  332. uni.hideLoading();
  333. console.log('res2', res2);
  334. uni.requestPayment({
  335. "provider": "wxpay",
  336. "orderInfo": {
  337. "appid": res2.data.appid, // 应用ID(AppID)
  338. "partnerid": res2.data.partnerId, // 商户号(PartnerID)
  339. "prepayid": res2.data.prepayId, // 预支付交易会话ID
  340. "package": res2.data.packageVal, // 固定值
  341. "noncestr": res2.data.nonceStr, // 随机字符串
  342. "timestamp": res2.data.timestamp, // 时间戳(单位:秒)
  343. "sign": res2.data.sign // 签名,这里用的 MD5 签名
  344. }, //此处为服务器返回的订单信息字符串
  345. success: function(res) {
  346. //var rawdata = JSON.parse(res.rawdata);
  347. // console.log('res',res);
  348. // console.log('支付成功');
  349. // console.log('rawdata', rawdata);
  350. uni.showLoading({
  351. title: '会员开通中,请稍后...'
  352. });
  353. orderCheck({
  354. id: orderId.value
  355. }).then(res3 => {
  356. console.log('res3', res3);
  357. if (res3.code == 0) {
  358. genggaiVip(res3.data)
  359. } else {
  360. setTimeout(() => {
  361. orderCheck({
  362. id: orderId.value
  363. }).then(res4 => {
  364. if (res4.code == 0) {
  365. genggaiVip(res4.data)
  366. } else {
  367. toast(
  368. "开通失败,请联系管理员!"
  369. )
  370. uni
  371. .hideLoading();
  372. return false
  373. }
  374. }).catch(() => {
  375. uni.hideLoading();
  376. toast("check接口报错")
  377. return false
  378. })
  379. }, 5000)
  380. }
  381. }).catch(() => {
  382. uni.hideLoading();
  383. toast("check接口报错")
  384. return false
  385. })
  386. },
  387. fail: function(err) {
  388. uni.hideLoading();
  389. // toast('支付失败:' + JSON.stringify(err));
  390. console.log('支付失败:' + JSON.stringify(err));
  391. }
  392. });
  393. }).catch((error) => {
  394. uni.hideLoading();
  395. // disabled.value = false
  396. console.log(error);
  397. })
  398. }
  399. function aliApy() {
  400. orderPayAli({
  401. id: orderId.value
  402. }).then(res2 => {
  403. console.log('res2', res2);
  404. uni.hideLoading();
  405. uni.requestPayment({
  406. "provider": "alipay",
  407. "orderInfo": res2.data.text, //此处为服务器返回的订单信息字符串
  408. success: function(res) {
  409. // var rawdata = JSON.parse(res.rawdata);
  410. // console.log('支付成功');
  411. // console.log('rawdata', rawdata);
  412. uni.showLoading({
  413. title: '会员开通中,请稍后...'
  414. });
  415. orderCheck({
  416. id: orderId.value
  417. }).then(res3 => {
  418. if (res3.code == 0) {
  419. genggaiVip(res3.data)
  420. } else {
  421. setTimeout(() => {
  422. orderCheck({
  423. id: orderId.value
  424. }).then(res4 => {
  425. if (res4.code ==
  426. 0) {
  427. genggaiVip(res4.data)
  428. } else {
  429. // disabled.value =false
  430. toast(
  431. "开通失败,请联系管理员!"
  432. )
  433. uni
  434. .hideLoading();
  435. return false
  436. }
  437. }).catch(() => {
  438. // disabled.value =false
  439. uni.hideLoading();
  440. toast("check接口报错")
  441. return false
  442. })
  443. }, 5000)
  444. }
  445. }).catch(() => {
  446. // disabled.value = false
  447. uni.hideLoading();
  448. toast("check接口报错")
  449. return false
  450. })
  451. },
  452. fail: function(err) {
  453. // disabled.value = false
  454. // toast('支付失败:' + JSON.stringify(err));
  455. console.log('支付失败:' + JSON.stringify(err));
  456. uni.hideLoading();
  457. }
  458. });
  459. })
  460. }
  461. const creatOrder = debounce((data) => {
  462. if (productData.selectedIds.length === 0) {
  463. toast("请选择至少一个商品");
  464. return;
  465. }
  466. // 获取选中商品的cardId列表
  467. const selectedProducts = productData.list.filter(
  468. item => productData.selectedIds.includes(item.id)
  469. );
  470. const cardIds = selectedProducts.map(item => item.id);
  471. console.log('cardIds', cardIds);
  472. disabled.value = true
  473. uni.showLoading({
  474. title: '',
  475. mask: true
  476. });
  477. if (payType.value == 'weixin') {
  478. console.log('创建订单11');
  479. if (orderId.value) {
  480. wxPay()
  481. } else {
  482. let req = {
  483. cardIds: cardIds
  484. }
  485. orderAdd(req).then(res => {
  486. console.log('res', res);
  487. console.log(' res.data.id', res.data.id);
  488. orderId.value = res.data.id
  489. wxPay()
  490. }).catch((err) => {
  491. uni.hideLoading();
  492. disabled.value = false
  493. toast("订单创建失败")
  494. return false
  495. })
  496. }
  497. } else {
  498. if (orderId.value) {
  499. aliApy()
  500. } else {
  501. let req = {
  502. cardIds: cardIds
  503. }
  504. orderAdd(req).then(res => {
  505. console.log('res', res);
  506. orderId.value = res.data.id
  507. aliApy()
  508. }).catch((err) => {
  509. disabled.value = false
  510. uni.hideLoading();
  511. toast("订单创建失败")
  512. return false
  513. })
  514. }
  515. }
  516. }, 500)
  517. function onChangeTab(e) {
  518. console.log('e', e);
  519. productData.current = e.currentIndex
  520. productData.selectedIds = [] // 清空选中
  521. getMore()
  522. }
  523. function getMore() {
  524. const opt = {
  525. subjectId: productData.current + 1
  526. }
  527. getMallist(opt).then(res => {
  528. if (res.code == 0) {
  529. productData.list = res.data
  530. // 初始化选中状态(当有外部cardId时)
  531. if (productData.cardId) {
  532. const exist = res.data.some(item => item.id == productData.cardId)
  533. if (exist) {
  534. productData.selectedIds = [Number(productData.cardId)]
  535. }
  536. }
  537. nextTick(() => {
  538. scrollToIdFun(productData.cardId)
  539. })
  540. }
  541. }).catch(err => {
  542. toast("获取产品数据失败")
  543. return false
  544. })
  545. }
  546. function scrollToIdFun(targetId) {
  547. const index = productData.list.findIndex(item => item.id == targetId)
  548. console.log('index', index);
  549. if (index > -1) {
  550. // 安卓设备需要双保险
  551. scrollIntoId.value = `item-${targetId}`
  552. setTimeout(() => {
  553. scrollIntoId.value = `item-${targetId}`
  554. }, 300)
  555. }
  556. }
  557. function addProduct(item) {
  558. console.log('item', item);
  559. const temp = [...productData.selectedIds]
  560. const index = temp.indexOf(item.id)
  561. index > -1 ? temp.splice(index, 1) : temp.push(item.id)
  562. productData.selectedIds = temp
  563. console.log('productData.selectedIds', productData.selectedIds);
  564. }
  565. function isItemSelected(id) {
  566. return productData.cardId ?
  567. id == productData.cardId // 有外部cardId时严格匹配
  568. :
  569. productData.selectedIds.includes(id) // 无cardId时检查选中列表
  570. }
  571. function goBack() {
  572. if (productData.from == 'daoPage') {
  573. uni.redirectTo({
  574. url: '/pages/study/index'
  575. })
  576. } else {
  577. uni.redirectTo({
  578. url: '/pages/my/index'
  579. })
  580. }
  581. }
  582. onLoad((options) => {
  583. if (options.from == 'daoPage') {
  584. productData.current = Number(options.subjectId) - 1
  585. productData.cardId = options.cardId
  586. productData.from = options.from
  587. } else {
  588. productData.current = Number(cacheManager.get('auth').subjectId) - 1
  589. }
  590. //isIOSorAndroid()
  591. getMore();
  592. })
  593. </script>
  594. <style>
  595. </style>