index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. <template>
  2. <view v-if="personal.user_id" class="container" :style="appThemeStyle">
  3. <view class="account-panel dis-flex flex-y-center">
  4. <view class="panel-lable">
  5. <text>账户余额</text>
  6. </view>
  7. <view class="panel-balance flex-box">
  8. <text>¥{{ personal.balance }}</text>
  9. </view>
  10. </view>
  11. <view class="recharge-panel">
  12. <view class="recharge-label">
  13. <text>充值金额</text>
  14. </view>
  15. <view class="recharge-plan clearfix">
  16. <block v-for="(item, index) in planList" :key="index">
  17. <view class="recharge-plan_item" :class="{active: selectedPlanId == item.plan_id}" @click="onSelectPlan(item.plan_id)">
  18. <view class="plan_money">
  19. <text>{{ item.money }}</text>
  20. </view>
  21. <view class="plan_gift">
  22. <text>送{{ item.gift_money }}</text>
  23. </view>
  24. </view>
  25. </block>
  26. </view>
  27. <!-- 手动充值输入框 -->
  28. <view class="recharge-input" v-if="setting.is_custom == 1">
  29. <input class="input" type="digit" placeholder="可输入自定义充值金额" v-model="inputValue" @input="onChangeMoney" />
  30. </view>
  31. <!-- 支付方式 -->
  32. <view class="recharge-label m-top60">
  33. <text>支付方式</text>
  34. </view>
  35. <view class="payment-method">
  36. <view v-for="(item, index) in methods" :key="index" class="pay-item dis-flex flex-x-between" @click="handleSelectPayType(index)">
  37. <view class="item-left dis-flex flex-y-center">
  38. <view class="item-left_icon" :class="[item.method]">
  39. <text class="iconfont" :class="[PayMethodIconEnum[item.method]]"></text>
  40. </view>
  41. <view class="item-left_text">
  42. <text>{{ PayMethodEnum[item.method].name }}</text>
  43. </view>
  44. </view>
  45. <view class="item-right col-m" v-if="curPaymentItem && curPaymentItem.method == item.method">
  46. <text class="iconfont icon-check"></text>
  47. </view>
  48. </view>
  49. </view>
  50. <!-- 确认按钮 -->
  51. <view class="recharge-submit btn-submit">
  52. <form @submit="onSubmit">
  53. <button class="button" formType="submit" :disabled="disabled">立即充值</button>
  54. </form>
  55. </view>
  56. </view>
  57. <!-- 充值描述 -->
  58. <view class="describe-panel">
  59. <view class="recharge-label">
  60. <text>充值说明</text>
  61. </view>
  62. <view class="content">
  63. <text space="ensp">{{ setting.describe }}</text>
  64. </view>
  65. </view>
  66. <!-- 支付确认弹窗 -->
  67. <!-- #ifdef H5 -->
  68. <u-modal
  69. v-if="tempUnifyData"
  70. v-model="showConfirmModal"
  71. title="支付确认"
  72. show-cancel-button
  73. confirm-text="已完成支付"
  74. :confirm-color="appTheme.mainBg"
  75. negative-top="100"
  76. :asyncClose="true"
  77. @confirm="onTradeQuery(tempUnifyData.outTradeNo, tempUnifyData.method)"
  78. >
  79. <view class="modal-content">
  80. <text>请在{{ PayMethodClientNameEnum[tempUnifyData.method] }}内完成支付,如果您已经支付成功,请点击“已完成支付”按钮</text>
  81. </view>
  82. </u-modal>
  83. <!-- #endif -->
  84. </view>
  85. </template>
  86. <script>
  87. import storage from '@/utils/storage'
  88. import * as RechargeApi from '@/api/recharge'
  89. import {PayMethodEnum} from '@/common/enum/payment'
  90. import {inArray, urlEncode} from '@/utils/util'
  91. import {Alipay, Wechat} from '@/core/payment'
  92. // 支付方式对应的图标
  93. const PayMethodIconEnum = {
  94. [PayMethodEnum.WECHAT.value]: 'icon-wechat-pay',
  95. [PayMethodEnum.ALIPAY.value]: 'icon-alipay'
  96. }
  97. // 支付方式的终端名称
  98. const PayMethodClientNameEnum = {
  99. [PayMethodEnum.WECHAT.value]: '微信',
  100. [PayMethodEnum.ALIPAY.value]: '支付宝'
  101. }
  102. // H5端支付下单时的数据
  103. // 用于从第三方支付页返回到收银台页面后拿到下单数据
  104. const getTempUnifyData = (orderKey) => {
  105. // if (window.performance && window.performance.navigation.type == 2) {
  106. const tempUnifyData = storage.get('tempUnifyData_' + orderKey)
  107. if (tempUnifyData) {
  108. storage.remove('tempUnifyData_' + orderKey)
  109. return tempUnifyData
  110. }
  111. // }
  112. return null
  113. }
  114. export default {
  115. data() {
  116. return {
  117. // 正在加载
  118. isLoading: true,
  119. // 按钮禁用
  120. disabled: false,
  121. // 枚举类
  122. PayMethodEnum,
  123. PayMethodIconEnum,
  124. PayMethodClientNameEnum,
  125. // 个人信息
  126. personal: {balance: '0.00'},
  127. // 充值设置
  128. setting: {},
  129. // 充值方案列表
  130. planList: [],
  131. // 当前客户端的支付方式列表(后端根据platform判断)
  132. methods: [],
  133. // 当前选中的套餐ID
  134. selectedPlanId: 0,
  135. // 自定义金额
  136. inputValue: '',
  137. // 当前选中的支付方式
  138. curPaymentItem: null,
  139. // 支付确认弹窗
  140. showConfirmModal: false,
  141. // #ifdef H5
  142. // 当前第三方支付信息 (临时数据, 仅用于H5端)
  143. tempUnifyData: {outTradeNo: '', method: ''}
  144. // #endif
  145. }
  146. },
  147. /**
  148. * 生命周期函数--监听页面显示
  149. */
  150. onShow(options) {
  151. // 获取页面数据
  152. this.getPageData()
  153. },
  154. methods: {
  155. // 选择充值套餐
  156. onSelectPlan(planId) {
  157. this.selectedPlanId = planId
  158. this.inputValue = ''
  159. },
  160. // 金额输入框
  161. onChangeMoney({detail}) {
  162. this.inputValue = detail.value
  163. this.selectedPlanId = 0
  164. },
  165. // 选择支付方式
  166. handleSelectPayType(index) {
  167. this.curPaymentItem = this.methods[index]
  168. },
  169. // 获取页面数据
  170. getPageData() {
  171. const app = this
  172. app.isLoading = true
  173. return new Promise((resolve, reject) => {
  174. RechargeApi.center({client: app.platform}).then((result) => {
  175. app.setting = result.data.setting
  176. app.personal = result.data.personal
  177. app.planList = result.data.planList
  178. app.methods = result.data.paymentMethods
  179. app.isLoading = false
  180. // 默认选中的支付方式
  181. app.setDefaultPayType()
  182. // #ifdef H5
  183. // 判断当前页面来源于浏览器返回
  184. this.performance()
  185. // #endif
  186. })
  187. })
  188. },
  189. // 默认选中的支付方式
  190. setDefaultPayType() {
  191. if (!this.curPaymentItem) {
  192. this.handleSelectPayType(0)
  193. }
  194. },
  195. // 判断当前页面来源于浏览器返回并提示手动查单
  196. // #ifdef H5
  197. performance() {
  198. const app = this
  199. const performanceData = getTempUnifyData('recharge')
  200. if (performanceData) {
  201. app.tempUnifyData = performanceData
  202. app.showConfirmModal = true
  203. }
  204. },
  205. // #endif
  206. // 立即充值
  207. onSubmit(e) {
  208. const app = this
  209. // 判断是否选择了支付方式
  210. if (!app.curPaymentItem) {
  211. app.$toast('您还没有选择支付方式')
  212. return
  213. }
  214. // 按钮禁用
  215. if (app.disabled) return
  216. app.disabled = true
  217. // 提交到后端
  218. RechargeApi.submit({
  219. planId: app.selectedPlanId,
  220. customMoney: app.inputValue ? app.inputValue : '',
  221. method: app.curPaymentItem.method,
  222. client: app.platform,
  223. extra: app.getExtraAsUnify(app.curPaymentItem.method)
  224. })
  225. .then((result) => app.onSubmitCallback(result))
  226. .finally((err) => {
  227. setTimeout(() => (app.disabled = false), 10)
  228. })
  229. },
  230. // 获取第三方支付的扩展参数
  231. getExtraAsUnify(method) {
  232. if (method === PayMethodEnum.ALIPAY.value) {
  233. return Alipay.extraAsUnify()
  234. }
  235. if (method === PayMethodEnum.WECHAT.value) {
  236. return Wechat.extraAsUnify()
  237. }
  238. return {}
  239. },
  240. // 订单提交成功后回调
  241. onSubmitCallback(result) {
  242. const app = this
  243. const method = app.curPaymentItem.method
  244. const paymentData = result.data.payment
  245. // 余额支付
  246. if (method === PayMethodEnum.BALANCE.value) {
  247. app.onShowSuccess(result)
  248. }
  249. // 发起支付宝支付
  250. if (method === PayMethodEnum.ALIPAY.value) {
  251. console.log('paymentData', paymentData)
  252. Alipay.payment({orderKey: 'recharge', ...paymentData})
  253. .then((res) => app.onPaySuccess(res))
  254. .catch((err) => app.onPayFail(err))
  255. }
  256. // 发起微信支付
  257. if (method === PayMethodEnum.WECHAT.value) {
  258. console.log('paymentData', paymentData)
  259. Wechat.payment({orderKey: 'recharge', ...paymentData})
  260. .then((res) => app.onPaySuccess(res))
  261. .catch((err) => app.onPayFail(err))
  262. }
  263. },
  264. // 订单支付成功的回调方法
  265. // 这里只是前端支付api返回结果success,实际订单是否支付成功 以后端的查单和异步通知为准
  266. onPaySuccess({res, option: {isRequireQuery, outTradeNo, method}}) {
  267. const app = this
  268. // 判断是否需要主动查单
  269. // isRequireQuery为true代表需要主动查单
  270. if (isRequireQuery) {
  271. app.onTradeQuery(outTradeNo, method)
  272. return true
  273. }
  274. this.onShowSuccess(res)
  275. },
  276. // 显示支付成功信息并页面跳转
  277. onShowSuccess({message}) {
  278. this.$toast(message || '订单支付成功')
  279. this.onSuccessNav()
  280. },
  281. // 订单支付失败
  282. onPayFail(err) {
  283. console.log('onPayFail', err)
  284. const errMsg = err.message || '订单未支付'
  285. this.$error(errMsg)
  286. },
  287. // 已完成支付按钮事件: 请求后端查单
  288. onTradeQuery(outTradeNo, method) {
  289. const app = this
  290. // 交易查询
  291. // 查询第三方支付订单是否付款成功
  292. RechargeApi.tradeQuery({outTradeNo, method, client: app.platform})
  293. .then((result) => (result.data.isPay ? app.onShowSuccess(result) : app.onPayFail(result)))
  294. .finally(() => (app.showConfirmModal = false))
  295. },
  296. // 支付成功后的跳转
  297. onSuccessNav() {
  298. const pages = getCurrentPages()
  299. const lastPage = pages.length < 2 ? null : pages[pages.length - 2]
  300. const backRoutes = ['pages/wallet/index']
  301. if (lastPage && inArray(lastPage.route, backRoutes)) {
  302. setTimeout(() => uni.navigateBack(), 1000)
  303. } else {
  304. setTimeout(() => {
  305. this.$navTo('pages/wallet/index', {}, 'redirectTo')
  306. }, 1200)
  307. }
  308. }
  309. }
  310. }
  311. </script>
  312. <style lang="scss" scoped>
  313. page,
  314. .container {
  315. background: #fff;
  316. }
  317. .container {
  318. padding-bottom: 70rpx;
  319. }
  320. .m-top60 {
  321. margin-top: 60rpx;
  322. }
  323. // 账户面板
  324. .account-panel {
  325. width: 650rpx;
  326. height: 180rpx;
  327. margin: 50rpx auto;
  328. padding: 0 60rpx;
  329. box-sizing: border-box;
  330. border-radius: 12rpx;
  331. color: #fff;
  332. background: linear-gradient(-125deg, #a46bff, #786cff);
  333. box-shadow: 0 5px 22px 0 rgba(0, 0, 0, 0.26);
  334. }
  335. .panel-lable {
  336. font-size: 32rpx;
  337. }
  338. .recharge-label {
  339. color: rgb(51, 51, 51);
  340. font-size: 30rpx;
  341. margin-bottom: 25rpx;
  342. }
  343. .panel-balance {
  344. text-align: right;
  345. font-size: 46rpx;
  346. }
  347. .recharge-panel {
  348. margin-top: 60rpx;
  349. padding: 0 60rpx;
  350. }
  351. // 充值套餐
  352. .recharge-plan {
  353. margin-bottom: -20rpx;
  354. .recharge-plan_item {
  355. width: 192rpx;
  356. padding: 15rpx 0;
  357. float: left;
  358. text-align: center;
  359. color: #888;
  360. border: 1rpx solid rgb(228, 228, 228);
  361. border-radius: 10rpx;
  362. margin: 0 20rpx 20rpx 0;
  363. &:nth-child(3n + 0) {
  364. margin-right: 0;
  365. }
  366. &.active {
  367. color: #786cff;
  368. border: 1rpx solid #786cff;
  369. .plan_money {
  370. color: #786cff;
  371. }
  372. }
  373. }
  374. }
  375. .plan_money {
  376. font-size: 32rpx;
  377. color: rgb(82, 82, 82);
  378. }
  379. .plan_gift {
  380. font-size: 25rpx;
  381. }
  382. .recharge-input {
  383. margin-top: 40rpx;
  384. .input {
  385. display: block;
  386. border: 1rpx solid rgb(228, 228, 228);
  387. border-radius: 10rpx;
  388. padding: 20rpx 26rpx;
  389. font-size: 28rpx;
  390. }
  391. }
  392. // 立即充值
  393. .recharge-submit {
  394. margin-top: 70rpx;
  395. }
  396. .btn-submit {
  397. .button {
  398. font-size: 30rpx;
  399. background: #786cff;
  400. border: none;
  401. color: white;
  402. border-radius: 50rpx;
  403. padding: 0 120rpx;
  404. line-height: 3;
  405. }
  406. .button[disabled] {
  407. background: #a098ff;
  408. border-color: #a098ff;
  409. color: white;
  410. }
  411. }
  412. // 充值说明
  413. .describe-panel {
  414. margin-top: 50rpx;
  415. padding: 0 60rpx;
  416. .content {
  417. font-size: 26rpx;
  418. line-height: 1.6;
  419. color: #888;
  420. }
  421. }
  422. // 支付方式
  423. .payment-method {
  424. .pay-item {
  425. padding: 14rpx 0;
  426. font-size: 26rpx;
  427. .item-left_icon {
  428. margin-right: 20rpx;
  429. font-size: 44rpx;
  430. &.wechat {
  431. color: #00c800;
  432. }
  433. &.alipay {
  434. color: #009fe8;
  435. }
  436. }
  437. .item-left_text {
  438. font-size: 30rpx;
  439. }
  440. .item-right {
  441. font-size: 30rpx;
  442. }
  443. .user-balance {
  444. margin-left: 20rpx;
  445. font-size: 26rpx;
  446. }
  447. }
  448. }
  449. // 支付确认弹窗
  450. .modal-content {
  451. padding: 40rpx 48rpx;
  452. font-size: 30rpx;
  453. line-height: 50rpx;
  454. text-align: left;
  455. color: #606266;
  456. // height: 620rpx;
  457. box-sizing: border-box;
  458. }
  459. </style>