pay-qrcode.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <template>
  2. <view>
  3. <view class="code-container " style="margin: 60rpx 35rpx;">
  4. <view class="title center">{{isPayment? "向商家付款":"扫一扫,向我付款"}}</view>
  5. <view class="main-content">
  6. <view v-if="isPayment">
  7. <!-- 占位 -->
  8. <view style="height: 50rpx;"></view>
  9. <!-- 条形码 -->
  10. <view class="bar-code center" style="overflow-x: hidden;margin: 0 30rpx;">
  11. <tki-barcode ref="barcode" :val="content" onval=true :opations="barOption" />
  12. </view>
  13. <!-- 付款码 -->
  14. <view class="qr-code center">
  15. <tki-qrcode cid="2" ref="qrcode" :icon="avatar" :val="content" size="350" :onval="true" :loadMake="true"
  16. :usingComponents="true" />
  17. </view>
  18. </view>
  19. <!-- 收款码 -->
  20. <view v-else class="charge-code center">
  21. <tki-qrcode cid="1" ref="qrcode-charge" :val="id" :icon="avatar" size="500" :onval="true"
  22. :loadMake="true" :usingComponents="true" />
  23. </view>
  24. </view>
  25. <view class="footer center flex-direction" :style="$isNotEmpty(vuex_channel)?'padding: 30rpx 0rpx;':''">
  26. <text class="text-xl text-bold">{{nickName}}</text>
  27. <!-- #ifdef MP-WEIXIN-->
  28. <view style="width: 100%;padding: 0;">
  29. <channel-list></channel-list>
  30. </view>
  31. <!-- #endif -->
  32. </view>
  33. </view>
  34. </view>
  35. </template>
  36. <script>
  37. import channelList from '@/components/channel-list.vue'
  38. import tkiQrcode from "../../comps/tki-qrcode/tki-qrcode.vue"
  39. import tkiBarcode from "../../comps/tki-barcode/tki-barcode.vue"
  40. import totp from "../../utils/totp.js"
  41. import socket from "../../utils/socket.js"
  42. import crypto from "@/utils/crypto.js"
  43. export default {
  44. components: {
  45. channelList,
  46. tkiBarcode,
  47. tkiQrcode
  48. },
  49. computed: {
  50. content() {
  51. let transformStr = this.id
  52. let qrcodeText=transformStr + this.secret + ";" + (this.vuex_channel ? this.vuex_channel.id : 0)
  53. return crypto.encrypt(qrcodeText)
  54. }
  55. },
  56. data() {
  57. return {
  58. isFirst:true,
  59. id: '',
  60. avatar: "",
  61. nickName: '',
  62. secret: '000000',
  63. isPayment: true,
  64. timer: '',
  65. barOption: {
  66. width: 1,
  67. height: 120,
  68. displayValue: false
  69. },
  70. //webSocket
  71. webSocket: {},
  72. receiveId: '',
  73. payResult: {
  74. isSuccess: false,
  75. msg: "交易失败",
  76. //总价
  77. totalPrice: 0,
  78. //消耗积分值
  79. pointsNum: 0,
  80. //消耗现金值
  81. amountNum: 0,
  82. }
  83. }
  84. },
  85. async onLoad(options) {
  86. uni.setKeepScreenOn({
  87. keepScreenOn: true
  88. });
  89. let res = await this.$api.loginUser.detail({
  90. id: this.vuex_userId
  91. })
  92. this.nickName = res.data.nickName
  93. // this.avatar =(await this.initAvatat(res.data.avatar)).tempFilePath;
  94. //连接websocket
  95. this.onSocketOpen()
  96. },
  97. async mounted() {
  98. //获取用户ID
  99. this.id = this.vuex_userId
  100. //生成动态密码
  101. if (this.isFirst) {
  102. this.secret = totp.getSecret(this.id);
  103. console.log(this.secret);
  104. }
  105. this.isFirst=false
  106. this.refreshCode();
  107. },
  108. onUnload() {
  109. this.handelOut()
  110. },
  111. onHide() {
  112. this.handelOut()
  113. },
  114. onBackPress() {
  115. this.handelOut()
  116. },
  117. methods: {
  118. handelOut() {
  119. uni.hideLoading()
  120. this.webSocket.closeSocket()
  121. clearInterval(this.timer);
  122. },
  123. // 启动webSocket
  124. onSocketOpen() {
  125. this.webSocket = new socket({
  126. sid: this.$global.socket_prefix + this.vuex_userId
  127. })
  128. this.webSocket.init(() => {
  129. console.log("启动成功");
  130. });
  131. this.webSocket.error = () => {
  132. this.webSocket.closeSocket();
  133. this.$dialog.showModalAndBack('网络异常')
  134. }
  135. this.webSocket.acceptMessage = (res) => {
  136. uni.hideLoading()
  137. if (!this.$isEmpty(res.price)) {
  138. this.receiveId = res.receiveId
  139. this.payResult.totalPrice = res.cost
  140. this.payResult.amountNum = res.price
  141. this.payResult.pointsNum = this.$digital.floatSub(res.cost, res.price)
  142. //调起微信支付
  143. this.toPay(res.id)
  144. }
  145. if (res.isSuccess) {
  146. console.log(res);
  147. this.payResult = res
  148. this.payResult.pointsNum = res.totalPrice
  149. this.payResult.amountNum = 0
  150. uni.navigateTo({
  151. url: "/pagesC/pages/checkstand/pay-result?payResult=" + JSON.stringify(res),
  152. })
  153. }
  154. }
  155. },
  156. refreshCode() {
  157. this.timer = setInterval(() => {
  158. this.secret = totp.getSecret(this.id);
  159. console.log(this.secret);
  160. }, 1000)
  161. },
  162. getSecret(){
  163. this.secret = totp.getSecret(this.id);
  164. },
  165. async initAvatat(url) {
  166. return new Promise((reslove, reject) => {
  167. uni.downloadFile({
  168. url,
  169. success: (res) => {
  170. reslove(res);
  171. },
  172. fail: (err) => {
  173. reject(err)
  174. }
  175. })
  176. })
  177. },
  178. async toPay(orderId) {
  179. //去支付
  180. let params = {
  181. orderType: this.$global.orderType.USER_PAY,
  182. orderId,
  183. payStatus: this.$global.payStatus.IS_WAIT
  184. }
  185. let res = await this.$api.pay.payOrder(params)
  186. if (!this.$isEmpty(res.data.prePayTn)) {
  187. let prePayTn = JSON.parse(res.data.prePayTn)
  188. this.$mpi.requestPayment(prePayTn).then(() => {
  189. this.handelResult(true)
  190. }).catch(err => {
  191. this.handelResult(false)
  192. })
  193. return
  194. }
  195. this.handelResult(false)
  196. },
  197. handelResult(flag) {
  198. if (flag) {
  199. this.payResult.msg = '交易成功'
  200. this.payResult.isSuccess = true
  201. } else {
  202. this.payResult.msg = '交易失败'
  203. this.payResult.isSuccess = false
  204. }
  205. this.sendInfo()
  206. uni.navigateTo({
  207. url: "/pagesC/pages/checkstand/pay-result?payResult=" + JSON.stringify(this.payResult)
  208. })
  209. },
  210. sendInfo() {
  211. let msg = {
  212. sid: this.$global.socket_prefix_SHOP + this.receiveId,
  213. content: JSON.stringify(this.payResult),
  214. }
  215. this.$api.webSocket.sendInfo(msg)
  216. },
  217. },
  218. }
  219. </script>
  220. <style>
  221. page {
  222. background-color: #0aa98b;
  223. }
  224. </style>
  225. <style lang="scss" scoped>
  226. $color:#0aa98b;
  227. .popup-content {
  228. display: flex;
  229. flex-direction: column;
  230. justify-content: space-between;
  231. padding: 30rpx 0;
  232. height: 100%;
  233. .channelbtn {
  234. background-color: $color;
  235. color: #FFFFFF;
  236. padding: 36rpx 150rpx;
  237. }
  238. .channel-item {
  239. position: relative;
  240. display: flex;
  241. padding: 30rpx;
  242. border-bottom: 1rpx solid #eee;
  243. image {
  244. width: 40rpx;
  245. height: 40rpx;
  246. margin-right: 10rpx;
  247. }
  248. .checked {
  249. color: $color;
  250. font-size: 34rpx;
  251. position: absolute;
  252. font-weight: 800;
  253. right: 40rpx;
  254. bottom: 30rpx;
  255. }
  256. }
  257. }
  258. .channel {
  259. display: flex;
  260. justify-content: space-between;
  261. margin-top: 40rpx;
  262. .left {
  263. display: flex;
  264. image {
  265. width: 60rpx;
  266. height: 60rpx;
  267. display: flex;
  268. justify-content: center;
  269. align-items: center;
  270. }
  271. view {
  272. display: flex;
  273. flex-direction: column;
  274. }
  275. .content {
  276. margin-left: 16rpx;
  277. text:first-child {
  278. font-size: 34rpx;
  279. margin-bottom: 10rpx;
  280. }
  281. text:last-child {
  282. font-size: 28rpx;
  283. color: #989898;
  284. }
  285. }
  286. }
  287. .right {
  288. display: flex;
  289. justify-content: center;
  290. align-items: center;
  291. }
  292. }
  293. .code-container {
  294. background-color: #FFFFFF;
  295. border-radius: 16rpx;
  296. .title {
  297. border-radius: 25rpx 25rpx 0 0;
  298. height: 100rpx;
  299. width: 100%;
  300. font-size: 35rpx;
  301. color: $color;
  302. background-color: #F7F7F7;
  303. font-weight: 600;
  304. letter-spacing: 3rpx;
  305. }
  306. .footer {
  307. width: 100%;
  308. padding:40rpx 0rpx;
  309. border-top: 1rpx dashed #dddddd;
  310. }
  311. .main-content {
  312. width: 100%;
  313. height: 700rpx;
  314. // overflow: hidden;
  315. .bar-code {
  316. height: 170rpx;
  317. }
  318. .qr-code {
  319. width: 100%;
  320. height: 500rpx;
  321. }
  322. .charge-code {
  323. width: 100%;
  324. height: 600rpx;
  325. transition: transform 1s ease-in-out;
  326. }
  327. }
  328. }
  329. </style>