buy.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. <template>
  2. <div v-loading="isLoading" class="container">
  3. <div class="title">购买会员</div>
  4. <!-- 会员套餐 -->
  5. <div class="section">
  6. <div class="subtitle">会员套餐<span class="tip">(基础服务功能)</span></div>
  7. <radio-card
  8. v-model="checkedPackage"
  9. :list="packageList"
  10. :show-un-active-icon="false"
  11. @change="handlePackageChange"
  12. >
  13. <template #item="{ row }">
  14. <div class="vip-package">
  15. <div class="time">{{ row.duration + (row.unit === 1 ? '月' : '年') }}</div>
  16. <div>
  17. <span>¥</span>
  18. <span class="price">{{ row.price }}</span>
  19. <span class="deleted">¥{{ row.originalPrice }}</span>
  20. </div>
  21. </div>
  22. </template>
  23. </radio-card>
  24. </div>
  25. <el-divider class="divider" />
  26. <!-- 订制服务 -->
  27. <div class="section">
  28. <div class="subtitle">订制服务<span class="tip">(勾选且付费后,将会有专人与您进行沟通)</span></div>
  29. <radio-card v-model="checkedServiceList" :list="serviceList" type="checkbox" @change="handleServiceChange">
  30. <template #item="{ row }">
  31. <div class="service-package">
  32. <div class="name">{{ row.name }}</div>
  33. <div class="subname">定制要求与费用另议</div>
  34. </div>
  35. </template>
  36. </radio-card>
  37. </div>
  38. <el-divider class="divider" />
  39. <template v-if="paySwitch">
  40. <!-- 选择支付方式 -->
  41. <div class="section">
  42. <div class="subtitle">选择支付方式</div>
  43. <radio-card v-model="checkedPayWay" :list="payWayList" :border="false" :show-un-active-icon="false">
  44. <template #item="{ row }">
  45. <el-image :src="row.image" class="pay-way" />
  46. </template>
  47. </radio-card>
  48. </div>
  49. <!-- 支付 -->
  50. <!-- 阿里云支付 -->
  51. <pay-alipay
  52. v-if="checkedPayWay === 1"
  53. ref="aliPay"
  54. class="pay-code"
  55. :data="orderInfo"
  56. :pay-way="checkedPayWay"
  57. @pay-confirm="handlePayConfirm"
  58. @hook:created="handleScanPayStart"
  59. />
  60. <!-- 微信支付 -->
  61. <pay-wechat
  62. v-if="checkedPayWay === 2"
  63. ref="wechatPay"
  64. class="pay-code"
  65. :data="orderInfo"
  66. :pay-way="checkedPayWay"
  67. @pay-confirm="handlePayConfirm"
  68. @hook:created="handleScanPayStart"
  69. />
  70. <!-- 企业网银支付 & 个人网银支付-->
  71. <template v-if="checkedPayWay === 3 || checkedPayWay === 4">
  72. <pay-bank
  73. :key="checkedPayWay"
  74. class="pay-bank"
  75. :data="orderInfo"
  76. :pay-way="checkedPayWay"
  77. @pay-confirm="handlePayConfirm"
  78. @hook:created="handleOtherPayStart"
  79. />
  80. </template>
  81. </template>
  82. <template v-else>
  83. <el-alert show-icon title="支付系统遇到点小问题,请稍后重试或联系管理员处理" type="error" effect="dark" />
  84. </template>
  85. </div>
  86. </template>
  87. <script>
  88. // 图标
  89. import imgPayAli from '@/assets/pay/pay-ali.png'
  90. import imgBank from '@/assets/pay/pay-bank.png'
  91. import imgSelfBank from '@/assets/pay/pay-self-bank.png'
  92. import imgWechat from '@/assets/pay/pay-wechat.png'
  93. import PayAlipay from '@/views/components/payment/pay-alipay.vue'
  94. import PayWechat from '@/views/components/payment/pay-wechat.vue'
  95. import PayBank from '@/views/components/payment/pay-bank.vue'
  96. // 单选 / 多选
  97. import RadioCard from '@/views/components/RadioCard'
  98. // 接口
  99. import { fetchConfigureList } from '@/api/member'
  100. import { registerSuperPay, payOnlineChecked, checkedWinxinPaySuccess, checkedOtherPaySuccess } from '@/api/pay'
  101. export default {
  102. components: {
  103. RadioCard,
  104. PayAlipay,
  105. PayWechat,
  106. PayBank
  107. },
  108. data() {
  109. return {
  110. isLoading: true,
  111. orderInfo: {
  112. pageType: 4,
  113. payAmount: '',
  114. vipId: '',
  115. vipRecordId: ''
  116. },
  117. packageList: [],
  118. serviceList: [],
  119. checkedPackage: '',
  120. checkedServiceList: [],
  121. checkedPayWay: 0,
  122. paySwitch: true,
  123. redirectUrlQuery: {},
  124. // 扫码支付倒计时
  125. scanToPayStartTime: 0,
  126. scanToPayEndTime: 0,
  127. payType: '',
  128. payWayList: [
  129. {
  130. image: imgPayAli,
  131. name: '阿里云支付',
  132. id: 1,
  133. checked: true
  134. },
  135. {
  136. image: imgWechat,
  137. name: '微信支付',
  138. id: 2,
  139. checked: false
  140. },
  141. {
  142. image: imgBank,
  143. name: '企业网银支付',
  144. id: 3,
  145. checked: false
  146. },
  147. {
  148. image: imgSelfBank,
  149. name: '个人网银支付',
  150. id: 4,
  151. checked: false
  152. }
  153. ]
  154. }
  155. },
  156. created() {
  157. this.fetchConfigureList()
  158. this.payOnlineChecked()
  159. },
  160. methods: {
  161. // 获取套餐 服务配置列表
  162. fetchConfigureList() {
  163. fetchConfigureList()
  164. .then((res) => {
  165. this.packageList = res.data.packageList
  166. this.serviceList = res.data.serviceList
  167. // 设置初始值
  168. this.checkedPackage = this.packageList[0].id
  169. this.registerSuperPay()
  170. this.setOrderInfo()
  171. })
  172. .finally(() => {
  173. this.isLoading = false
  174. })
  175. },
  176. // 设置订单信息(这个假订单)
  177. setOrderInfo() {
  178. const findOne = this.packageList.find((item) => item.id === this.checkedPackage)
  179. if (findOne) {
  180. this.orderInfo.payAmount = findOne.price
  181. this.orderInfo.vipId = findOne.id
  182. }
  183. },
  184. // 套餐修改
  185. handlePackageChange() {
  186. this.checkedPayWay = 0
  187. this.registerSuperPay()
  188. this.setOrderInfo()
  189. },
  190. // 服务修改
  191. handleServiceChange() {
  192. console.log(this.serviceList)
  193. this.checkedPayWay = 0
  194. this.registerSuperPay()
  195. this.setOrderInfo()
  196. },
  197. // 在线支付开通会员预处理
  198. registerSuperPay() {
  199. registerSuperPay({
  200. authUserId: this.$store.getters.authUserId,
  201. packageId: this.checkedPackage,
  202. services: this.checkedServiceList.join(',')
  203. }).then((res) => {
  204. this.orderInfo.vipRecordId = res.data
  205. })
  206. },
  207. // 在线支付开关检测
  208. payOnlineChecked() {
  209. payOnlineChecked().then((res) => {
  210. if (res.data === '1') {
  211. this.paySwitch = true
  212. } else {
  213. this.paySwitch = false
  214. }
  215. })
  216. },
  217. // 确认支付是否完成
  218. handlePayConfirm(e) {
  219. this.redirectUrlQuery = {
  220. payAmount: e.data.payAmount
  221. }
  222. this.payType = e.type
  223. if (e.type === 'WEIXIN') {
  224. this.checkedWinxinPaySuccess({ vipRecordId: e.data.vipRecordId, orderFlag: 0 })
  225. } else {
  226. this.checkedOtherPaySuccess({ mbOrderId: e.data.mbOrderId, orderFlag: 0 })
  227. }
  228. },
  229. // 查询是否成功(微信支付专用)
  230. checkedWinxinPaySuccess(params) {
  231. checkedWinxinPaySuccess(params).then((res) => {
  232. this.handleWixinPayResult(res)
  233. })
  234. },
  235. // 查询支付是否成功 (支付宝 银联)
  236. checkedOtherPaySuccess(params) {
  237. checkedOtherPaySuccess(params).then((res) => {
  238. this.handlePayResult(res)
  239. })
  240. },
  241. // 处理微信支付查询结果
  242. handleWixinPayResult(result) {
  243. result = JSON.parse(result.data)
  244. if (result.data.status === '1') {
  245. this.$store.dispatch('user/fetchUserVipInfo')
  246. this.$router.push({
  247. path: '/pay/success',
  248. query: this.redirectUrlQuery
  249. })
  250. } else {
  251. this.diffScanOverTime(60 * 5, () => {
  252. this.$router.push({
  253. path: '/pay/faild',
  254. query: this.redirectUrlQuery
  255. })
  256. })
  257. }
  258. },
  259. // 处理网银支付宝支付查询结果
  260. handlePayResult(result) {
  261. result = JSON.parse(result.data)
  262. console.log(result)
  263. if (result.rt7_orderStatus === 'SUCCESS') {
  264. this.$store.dispatch('user/fetchUserVipInfo')
  265. this.$router.push({
  266. path: '/pay/success',
  267. query: this.redirectUrlQuery
  268. })
  269. } else if (result.rt7_orderStatus === 'DOING') {
  270. this.diffScanOverTime(60 * 5, () => {
  271. this.$router.push({
  272. path: '/pay/faild',
  273. query: this.redirectUrlQuery
  274. })
  275. })
  276. } else {
  277. this.$router.push({
  278. path: '/pay/faild',
  279. query: this.redirectUrlQuery
  280. })
  281. }
  282. },
  283. // 扫码支付开始
  284. handleScanPayStart() {
  285. const h = this.$createElement
  286. this.$notify({
  287. title: '扫码支付提示',
  288. message: h('span', { style: 'color: #ff6d6d' }, '请在5分钟内完成扫码支付')
  289. })
  290. this.scanToPayStartTime = new Date().getTime()
  291. console.log('扫码支付开始', this.scanToPayStartTime)
  292. },
  293. // 其他支付方式(非扫码支付)
  294. handleOtherPayStart() {
  295. this.scanToPayStartTime = 0
  296. },
  297. diffScanOverTime(time, callback) {
  298. if (this.scanToPayStartTime > 0) {
  299. this.scanToPayEndTime = new Date().getTime()
  300. const subTime = this.scanToPayEndTime - this.scanToPayStartTime
  301. const maxTime = time * 1000
  302. if (subTime > maxTime) {
  303. console.log('放行')
  304. callback()
  305. }
  306. } else {
  307. console.log('放行')
  308. callback()
  309. }
  310. }
  311. }
  312. }
  313. </script>
  314. <style scoped lang="scss">
  315. .container {
  316. margin: 0 auto;
  317. padding: 0 60px;
  318. }
  319. .divider {
  320. margin: 40px 0;
  321. }
  322. .pay-code {
  323. margin: 40px 0;
  324. }
  325. .pay-bank {
  326. margin: 25px 0;
  327. }
  328. .title {
  329. margin: 40px 0;
  330. font-size: 24px;
  331. font-weight: bold;
  332. color: #404040;
  333. text-align: center;
  334. }
  335. .section {
  336. .subtitle {
  337. margin-bottom: 24px;
  338. font-size: 18px;
  339. color: #101010;
  340. .tip {
  341. font-size: 14px;
  342. color: #909399;
  343. }
  344. }
  345. .vip-package {
  346. height: 100%;
  347. display: flex;
  348. justify-content: center;
  349. align-items: center;
  350. flex-direction: column;
  351. width: 232px;
  352. height: 128px;
  353. .time {
  354. margin-bottom: 16px;
  355. font-size: 18px;
  356. font-weight: bold;
  357. color: #404040;
  358. }
  359. span {
  360. color: #ff6d6d;
  361. }
  362. .price {
  363. font-size: 36px;
  364. font-weight: bold;
  365. color: #ff6d6d;
  366. }
  367. .deleted {
  368. font-size: 14px;
  369. color: #b2b2b2;
  370. text-decoration: line-through;
  371. }
  372. }
  373. .service-package {
  374. height: 100%;
  375. display: flex;
  376. justify-content: center;
  377. align-items: center;
  378. flex-direction: column;
  379. width: 232px;
  380. height: 128px;
  381. .name {
  382. margin-bottom: 16px;
  383. font-size: 18px;
  384. color: #404040;
  385. }
  386. .subname {
  387. font-size: 14px;
  388. color: #ff6d6d;
  389. }
  390. }
  391. .pay-way {
  392. display: block;
  393. width: 128px;
  394. height: 44px;
  395. }
  396. }
  397. </style>