index.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <template>
  2. <div style="width: 70%;margin:0 auto">
  3. <div class="fx-row jc-between ai-center pd10 pt10 mb20">
  4. <el-image class="pointer" @click="$router.push('/login')" :src="`${$imgBase}index/login/mxjc_img_logo.png`"
  5. fit="contain"
  6. style="width: 152px;"></el-image>
  7. <div>
  8. <img class="icon16" src="../../assets/images/icon_tel2.png"/>
  9. <span class="pl8">服务热线:400-0313-985</span>
  10. </div>
  11. </div>
  12. <el-form ref="form" class="border pd20" :model="form" :rules="rules" label-width="100px" label-position="right">
  13. <p class="f24 pb20 f-primary bold text-left">铭学锦程学生卡</p>
  14. <el-row :gutter="20">
  15. <el-col :span="12">
  16. <el-image :src="require('../../assets/images/pay_banner.png')"/>
  17. </el-col>
  18. <el-col :span="12">
  19. <el-form-item label="价格:"><span v-if="cardActive.price" class="bold f-red f18">
  20. ¥{{ cardActive.price / 100 }}</span>
  21. </el-form-item>
  22. <el-form-item label="有效期:">{{ cardActive.outTime }}</el-form-item>
  23. <el-form-item label="入学年份:">
  24. <el-button @click="cardActive = card" :type="card == cardActive ? 'primary' : ''" v-for="card in cardList">
  25. {{ card.year }}
  26. </el-button>
  27. </el-form-item>
  28. <template v-if="!renew">
  29. <el-form-item label="手机号:" prop="phoneNumber">
  30. <el-input v-model="form.phoneNumber"></el-input>
  31. </el-form-item>
  32. <el-form-item label="验证码:" prop="captcha">
  33. <div class="fx-row">
  34. <el-input class="mr30" v-model="form.captcha"></el-input>
  35. <el-button type="plain" @click="getCode" :disabled="disabled">{{ captchaBtnText }}</el-button>
  36. </div>
  37. </el-form-item>
  38. </template>
  39. <template v-else>
  40. <el-form-item label="原卡号:" required>
  41. {{ renew.cardNo }}
  42. </el-form-item>
  43. <el-form-item label="绑定手机:" required>
  44. {{ renew.mobile }}
  45. </el-form-item>
  46. </template>
  47. <el-form-item>
  48. <el-button type="primary" @click="validate">{{ announcement.button }}</el-button>
  49. </el-form-item>
  50. </el-col>
  51. </el-row>
  52. </el-form>
  53. <div class="tips ">
  54. <p class="f20 bold f-red">{{announcement.title}}</p>
  55. <p v-for="(r,idx) in announcement.rules" :key="idx">{{idx+1}}、{{r}}</p>
  56. </div>
  57. <el-dialog :visible.sync="dialogVisible" :close-on-click-modal="false" custom-class="rd8" width="340px">
  58. <template #title>
  59. <div class="fx-column fx-cen-cen">
  60. <div class="f16">使用微信扫码支付</div>
  61. <div class="f-red f18">¥{{ cardActive.price / 100 }}</div>
  62. </div>
  63. </template>
  64. <el-image :src="payQR" style="width: 300px; height: 300px">
  65. <template v-if="isPaySuccess" #error>
  66. <div class="fx-column fx-cen-cen" style="padding-top: 50px">
  67. <el-icon class="f-success" name="success" style="font-size: 96px"/>
  68. <div class="mt20 f-red">{{ announcement.paySucceed }}</div>
  69. <div class="mt8">
  70. <el-link href="/login">点击此处,去登陆</el-link>
  71. </div>
  72. </div>
  73. </template>
  74. <template v-else-if="isPayFailed" #error>
  75. <div class="fx-column fx-cen-cen" style="padding-top: 50px">
  76. <el-icon class="f-danger" name="error" style="font-size: 96px"/>
  77. <div class="mt20 f-red">{{ announcement.payFailed }}</div>
  78. <el-button type="text" class="mt8 f-666" @click="form.captcha='',dialogVisible=false">关闭</el-button>
  79. </div>
  80. </template>
  81. </el-image>
  82. <template #footer>
  83. <div class="fx-column fx-cen-cen">
  84. <div>铭学锦程学习平台({{ cardActive.year }}学年)</div>
  85. <div>有效期至 <span class="bold">{{ cardActive.outTime }}</span></div>
  86. </div>
  87. </template>
  88. </el-dialog>
  89. </div>
  90. </template>
  91. <script>
  92. import { getEcardPrices, getOrderPayStatus, prepayCard, sendSmsNoValidation } from '@/api/webApi/pay'
  93. export default {
  94. name: 'PayIndex',
  95. data() {
  96. let checkPhone = (rule, value, callback) => {
  97. let reg = /^1[345789]\d{9}$/
  98. if (!reg.test(value)) {
  99. callback(new Error('手机号格式不正确!'))
  100. } else {
  101. callback()
  102. }
  103. }
  104. return {
  105. rules: {
  106. // required: true 是否必填,如不设置,则会根据校验规则自动生成
  107. phoneNumber: [
  108. { required: true, message: '请输入手机号', trigger: 'blur' },
  109. { type: 'number', validator: checkPhone, message: '请输入11位有效手机号号码', trigger: ['blur', 'change'] }
  110. ],
  111. captcha: { required: true, message: '短信验证码不能为空', trigger: 'change' }
  112. },
  113. form: {
  114. phoneNumber: '',
  115. captcha: ''
  116. },
  117. captchaBtnText: '免费获取',
  118. disabled: false,
  119. spaceTime: 60,
  120. cardActive: {},
  121. cardList: [],
  122. countdownTimer: null,
  123. // qr dialog
  124. dialogVisible: false,
  125. payQR: '',
  126. // pay/order status
  127. orderTimer: null,
  128. orderId: '',
  129. isPaySuccess: false,
  130. isPayFailed: false,
  131. isUnPaid: false,
  132. // announcements
  133. announcement: {
  134. title: '购卡须知',
  135. rules: [
  136. '您将购买的是电子卡,不同的入学年份有效期和价格不同。',
  137. '购买后系统会将账号与密码通过短信发送到手机,若忘记账号请联系客服。',
  138. '由于产品特性,本电子卡不适用“7天无理由退换货”。'
  139. ],
  140. paySucceed: '支付成功,账号已通过短信发送,请注意查收',
  141. payFailed: '支付失败,您可以重新发起支付',
  142. button: '购买'
  143. },
  144. // renew
  145. renew: null
  146. }
  147. },
  148. mounted() {
  149. this.getEcardPrices()
  150. },
  151. beforeDestroy() {
  152. this.stopOrderStatusTimer()
  153. this.stopCountdownTimer()
  154. },
  155. methods: {
  156. getCode() {
  157. this.$refs.form.clearValidate([])
  158. this.$refs.form.validateField('phoneNumber', (res) => {
  159. if (res == '') {
  160. // 通过
  161. sendSmsNoValidation({
  162. mobile: this.form.phoneNumber,
  163. smsType: 1
  164. }).then(res => {
  165. if (res.code == 200) {
  166. this.$message.success('发送成功,请在手机上查收')
  167. // 成功60秒不让点击
  168. this.disabled = true
  169. this.captchaBtnText = `(${this.spaceTime}秒)免费获取`
  170. this.countdownTimer = window.setInterval(() => {
  171. this.spaceTime--
  172. this.captchaBtnText = `(${this.spaceTime}秒)免费获取`
  173. if (this.spaceTime < 0) {
  174. this.stopCountdownTimer()
  175. this.captchaBtnText = '免费获取'
  176. this.totalTime = 60
  177. this.disabled = false
  178. }
  179. }, 1000)
  180. }
  181. })
  182. }
  183. })
  184. },
  185. getEcardPrices() {
  186. getEcardPrices(this.renew).then(res => {
  187. this.cardList = res.data
  188. this.cardActive = res?.data[0]
  189. })
  190. },
  191. validate() {
  192. this.$refs.form.validate(valid => {
  193. if (!valid) return
  194. prepayCard({
  195. mobile: this.form.phoneNumber,
  196. code: this.form.captcha,
  197. ecardPayId: this.cardActive.id,
  198. id: this.cardActive.id,
  199. amount: 1,
  200. ...this.renew
  201. }).then(res => {
  202. this.payQR = res.data.qrCode
  203. this.orderId = res.data.orderId
  204. this.dialogVisible = true
  205. this.stopOrderStatusTimer()
  206. this.startOrderStatusTimer()
  207. })
  208. })
  209. },
  210. startOrderStatusTimer() {
  211. if (this.orderTimer) return
  212. this.orderTimer = setInterval(() => {
  213. getOrderPayStatus({ orderId: this.orderId }).then(res => {
  214. const copyStatus = ['isPaySuccess', 'isPayFailed', 'isUnPaid']
  215. copyStatus.forEach(key => this[key] = res.data[key])
  216. const terminateStatus = ['isPaySuccess', 'isPayFailed']
  217. const terminate = terminateStatus.some(key => this[key])
  218. if (terminate) {
  219. this.stopOrderStatusTimer()
  220. this.payQR = ''
  221. }
  222. })
  223. }, 5000)
  224. },
  225. stopOrderStatusTimer() {
  226. if (this.orderTimer) {
  227. clearInterval(this.orderTimer)
  228. this.orderTimer = null
  229. }
  230. },
  231. stopCountdownTimer() {
  232. if (this.countdownTimer) {
  233. clearInterval(this.countdownTimer)
  234. this.countdownTimer = null
  235. }
  236. }
  237. }
  238. }
  239. </script>
  240. <style scoped>
  241. .border {
  242. border: 1px solid #e2e2e2;
  243. }
  244. >>> .el-form-item {
  245. margin-bottom: 20px;
  246. }
  247. .tips {
  248. padding: 20px 0;
  249. }
  250. .tips > p {
  251. margin-top: 10px;
  252. }
  253. /deep/ .el-dialog__body {
  254. padding-top: 0;
  255. padding-bottom: 0;
  256. }
  257. </style>