index.tsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. import request from '@/helpers/request'
  2. import {
  3. listenerMessage,
  4. postMessage,
  5. removeListenerMessage
  6. } from '@/helpers/native-message'
  7. import {
  8. Button,
  9. Cell,
  10. CellGroup,
  11. Icon,
  12. RadioGroup,
  13. Radio,
  14. Dialog,
  15. Toast
  16. } from 'vant'
  17. import { defineComponent, PropType } from 'vue'
  18. import styles from './index.module.less'
  19. import { state } from '@/state'
  20. import { orderStatus } from '../orderStatus'
  21. import { useEventTracking } from '@/helpers/hooks'
  22. import activeButtonIcon from '@common/images/icon_checkbox.png'
  23. import inactiveButtonIcon from '@common/images/icon_checkbox_default.png'
  24. import activeButtonIconTenant from '@common/images/icon_checkbox-tenant.png'
  25. import iconWechat from '@common/images/icon-wechat.png'
  26. import iconAlipay from '@common/images/icon-alipay.png'
  27. interface IOrderInfo {
  28. orderNo: string | number
  29. actualPrice: string | number
  30. }
  31. const urlFix =
  32. state.platformType === 'TEACHER' ? '/api-teacher' : '/api-student'
  33. const urlType = {
  34. goodsPay: {
  35. cancelUrl: '/api-mall-portal/order/cancelUserOrder',
  36. payUrl: '/api-mall-portal/payment/orderPay'
  37. },
  38. orderPay: {
  39. cancelUrl: urlFix + '/userOrder/orderCancel',
  40. payUrl: urlFix + '/userOrder/orderPay'
  41. }
  42. }
  43. export default defineComponent({
  44. name: 'payment',
  45. props: {
  46. modelValue: {
  47. type: Boolean,
  48. default: false
  49. },
  50. orderInfo: {
  51. type: Object as PropType<IOrderInfo>,
  52. default: {
  53. orderNo: '',
  54. actualPrice: 0
  55. }
  56. },
  57. onBackOut: {
  58. type: Function,
  59. default: () => {}
  60. },
  61. paymentType: {
  62. type: String,
  63. default: 'orderPay' as 'orderPay' | 'goodsPay'
  64. }
  65. },
  66. data() {
  67. return {
  68. payType: 'wx_app',
  69. pay_channel: ''
  70. }
  71. },
  72. unmounted() {
  73. removeListenerMessage('paymentOperation', this.paymentOperation)
  74. },
  75. mounted() {
  76. console.log(this.orderInfo, 'this.orderInfo')
  77. },
  78. methods: {
  79. onClose() {
  80. Dialog.confirm({
  81. message: '是否放弃本次付款',
  82. confirmButtonText: '继续付款',
  83. cancelButtonText: '放弃'
  84. })
  85. .then(() => {})
  86. .catch(async () => {
  87. this.onCancel()
  88. useEventTracking('取消支付')
  89. })
  90. },
  91. async onCancel(noBack?: boolean) {
  92. try {
  93. await request.post(urlType[this.paymentType].cancelUrl, {
  94. data: {
  95. orderNo: this.orderInfo.orderNo
  96. }
  97. })
  98. } catch {}
  99. // 不管接口是否报错,都返回
  100. this.$emit('update:modelValue', false)
  101. // 为了单独处理支付 曲目购买 && orderStatus.orderObject.orderType == 'MUSIC'
  102. // if (!noBack) {
  103. // postMessage({ api: 'back', content: {} })
  104. // return
  105. // }
  106. !noBack && this.$router.go(-1)
  107. this.onBackOut && this.onBackOut()
  108. },
  109. async onSubmit() {
  110. // 支付...
  111. try {
  112. const params = {
  113. orderNo: this.orderInfo.orderNo,
  114. payChannel: this.payType,
  115. paymentClient: null as any
  116. }
  117. if (this.paymentType === 'goodsPay') {
  118. params.paymentClient = state.platformType
  119. }
  120. const res = await request.post(urlType[this.paymentType].payUrl, {
  121. data: {
  122. ...params
  123. }
  124. })
  125. postMessage({
  126. api: 'paymentOrder',
  127. content: {
  128. orderNo: this.orderInfo.orderNo,
  129. payChannel: this.payType,
  130. // payInfo: `alipays://platformapi/startapp?saId=10000007&qrcode=${res.data.pay_info}`
  131. payInfo: res.data.pay_info
  132. }
  133. })
  134. Toast.loading({
  135. message: '支付中...',
  136. forbidClick: true,
  137. duration: 3000,
  138. loadingType: 'spinner'
  139. })
  140. Toast.clear()
  141. this.$emit('update:modelValue', false)
  142. // 唤起支付时状态
  143. listenerMessage('paymentOperation', result => {
  144. console.log(result, 'init paymentOperation')
  145. this.paymentOperation(result?.content)
  146. })
  147. } catch (e: any) {
  148. console.log(e)
  149. }
  150. useEventTracking('购买支付')
  151. },
  152. paymentOperation(res: any) {
  153. console.log(res, 'paymentOperation', this.paymentType, this.orderInfo)
  154. // 支付状态
  155. // paymentOperation 支付成功:success 支付失败:error 支付取消:cancel 未安装:fail
  156. // error 只有安卓端有
  157. if (res.status === 'success' || res.status === 'error') {
  158. Toast.clear()
  159. this.$emit('update:modelValue', false)
  160. if (this.paymentType === 'goodsPay') {
  161. this.$router.replace({
  162. path: '/shopTrade',
  163. query: {
  164. orderNo: this.orderInfo.orderNo
  165. }
  166. })
  167. return
  168. }
  169. this.$router.replace({
  170. path: '/tradeDetail',
  171. query: {
  172. orderNo: this.orderInfo.orderNo
  173. }
  174. })
  175. } else if (res.status === 'cancel') {
  176. Toast.clear()
  177. this.$emit('update:modelValue', false)
  178. } else if (res.status === 'fail') {
  179. const message =
  180. this.payType === 'ali_app' ? '您尚未安装支付宝' : '您尚未安装微信'
  181. Dialog.alert({
  182. title: '提示',
  183. message
  184. }).then(() => {
  185. Toast.clear()
  186. this.$emit('update:modelValue', false)
  187. })
  188. }
  189. }
  190. },
  191. render() {
  192. return (
  193. <div class={styles.payment}>
  194. <Icon onClick={this.onClose} name="cross" size={20} />
  195. <div class={[styles.title]}>选择支付方式</div>
  196. <div class={styles.payAmount}>
  197. <p>应付金额</p>
  198. <div class={styles.amount}>
  199. <span>¥</span>
  200. {(this as any).$filters.moneyFormat(this.orderInfo.actualPrice)}
  201. </div>
  202. </div>
  203. <RadioGroup v-model={this.payType}>
  204. <CellGroup border={false}>
  205. <Cell
  206. border={true}
  207. center
  208. onClick={() => {
  209. // wx_lite
  210. this.payType = 'wx_app'
  211. }}
  212. v-slots={{
  213. icon: () => <Icon name={iconWechat} size={18} />,
  214. 'right-icon': () => (
  215. <Radio
  216. name="wx_app"
  217. v-slots={{
  218. icon: (props: any) => (
  219. <Icon
  220. class={styles.boxStyle}
  221. name={
  222. props.checked
  223. ? state.projectType === 'tenant'
  224. ? activeButtonIconTenant
  225. : activeButtonIcon
  226. : inactiveButtonIcon
  227. }
  228. />
  229. )
  230. }}
  231. />
  232. ),
  233. title: () => (
  234. <div class={styles.payTypeRe}>
  235. 微信支付 <span class={styles.recommend}>推荐</span>
  236. </div>
  237. )
  238. }}
  239. ></Cell>
  240. <Cell
  241. title="支付宝支付"
  242. border={true}
  243. center
  244. onClick={() => {
  245. this.payType = 'ali_app'
  246. }}
  247. v-slots={{
  248. icon: () => <Icon name={iconAlipay} size={18} />,
  249. 'right-icon': () => (
  250. <Radio
  251. name="ali_app"
  252. v-slots={{
  253. icon: (props: any) => (
  254. <Icon
  255. class={styles.boxStyle}
  256. name={
  257. props.checked
  258. ? state.projectType === 'tenant'
  259. ? activeButtonIconTenant
  260. : activeButtonIcon
  261. : inactiveButtonIcon
  262. }
  263. />
  264. )
  265. }}
  266. />
  267. ),
  268. title: () => <div class={styles.payTypeRe}>支付宝支付</div>
  269. }}
  270. ></Cell>
  271. </CellGroup>
  272. </RadioGroup>
  273. <div class={styles.blank}></div>
  274. <Button
  275. type="primary"
  276. class={[
  277. styles.payBtn,
  278. state.projectType === 'tenant' && styles.tenantPayBtn
  279. ]}
  280. block
  281. round
  282. onClick={this.onSubmit}
  283. >
  284. 确认支付
  285. </Button>
  286. </div>
  287. )
  288. }
  289. })