You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

recharge.vue 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. <template>
  2. <view class="wrapper">
  3. <view class="account">
  4. <image :src="`${$imgUrl}common/bg-recharge.png`" class="head-bg"></image>
  5. <view class="account-text">
  6. <view class="balance">
  7. <!-- <text class="balance-tit">账户余额</text> -->
  8. <!-- <text class="balance-val">20.00元</text> -->
  9. </view>
  10. <view class="right-box">
  11. <view class="no">账户编号:000100</view>
  12. <view class="btn btn-record">账户充值记录</view>
  13. </view>
  14. </view>
  15. </view>
  16. <view class="input-box">
  17. <input placeholder="请输入金额" class="input" />
  18. <view class="tips">最低充值金额不小于10元</view>
  19. </view>
  20. <view class="tabs">
  21. <view class="tab-tit">在线充值</view>
  22. <view class="tab">
  23. <view class="item">50元</view>
  24. <view class="item active">100元</view>
  25. <view class="item">200元</view>
  26. <view class="item">300元</view>
  27. <view class="item">500元</view>
  28. <view class="item">1000元</view>
  29. </view>
  30. </view>
  31. <view class="btn btn-primary" @click="rechargeAction">立即充值</view>
  32. </view>
  33. </template>
  34. <script setup lang="ts">
  35. import {
  36. stringToJson
  37. } from "@/utils/network/encryption";
  38. import {
  39. onLoad,
  40. onShow
  41. } from "@dcloudio/uni-app";
  42. import {
  43. quanCheck,
  44. quanApply,
  45. quanXf,
  46. cardCzPayResultCheck,
  47. cardCzApply,
  48. cardCzPay,
  49. cardCzPayResult,
  50. cardCzXFCheck,
  51. quanConfirm,
  52. wechatAppID,
  53. wechatPayConfigId,
  54. wechatSecret,
  55. } from "@/utils/network/api.js";
  56. import {
  57. request
  58. } from "@/utils/network/request.js";
  59. import {
  60. reactive
  61. } from "vue";
  62. import {
  63. msg
  64. } from "@/utils/utils";
  65. import {
  66. getItem,
  67. StorageKeys,
  68. setItem
  69. } from "@/utils/storage";
  70. const datas = require("../../static/etcUtil/datas.js");
  71. const bluetoothUtil = require("../../static/etcUtil/index.js");
  72. const cmd = require("../../static/etcUtil/cmdConfig.js");
  73. const tools = require("../../static/etcUtil/tools.js");
  74. const state = reactive({
  75. openid: "",
  76. connectSuccess: undefined,
  77. fee: 1,
  78. orderNum: '',
  79. cardId: '', //卡号 需要传参
  80. mockpreBalance: 2000 //fix:模拟余额 目前没有检测状态接口,第一次会模拟圈层检测来检测状态
  81. });
  82. const card = reactive({
  83. cardId: "",
  84. netId: "",
  85. cardType: "",
  86. startTime: "",
  87. endTime: "",
  88. userName: "",
  89. idNum: "",
  90. vehiclePlate: "",
  91. vehiclePlateColor: "",
  92. color: "",
  93. version: "",
  94. type: "",
  95. favourable: "",
  96. money: undefined,
  97. v_userType: "",
  98. });
  99. onLoad((option) => {
  100. /*获取openId 用途:(用户支付)*/
  101. getOpenID();
  102. /*传参*/
  103. state.cardId = option.cardId
  104. state.connectSuccess = option.connectSuccess
  105. state.orderNum = getItem("orderNum")
  106. if (state.connectSuccess === '1') {
  107. /*读卡*/
  108. getCardId()
  109. }
  110. });
  111. /*点击充值按钮*/
  112. const rechargeAction = () => {
  113. /*进行圈层检测,此处检测仅为校验圈层状态来决定去哪里*/
  114. console.log('进行圈层检测')
  115. quanCheckAction().then((val: any) => {
  116. //如果 圈层检测正常
  117. if (val.chargeStatus === 1) {
  118. console.log(`进行充值检测`)
  119. czCheckAction().then((checkResult: any) => {
  120. //判断订单逻辑如果有订单充值 则直接拿到订单去链接蓝牙
  121. if (checkResult.orders && checkResult.orders.length > 0) {
  122. //拿到订单,存起来
  123. state.orderNum = checkResult.orders[0].orderNum
  124. setItem("orderNum", state.orderNum)
  125. //链接蓝牙
  126. uni.navigateTo({
  127. url: `/pages/bluetooth/device-active-step3?routeType=2`,
  128. });
  129. } else {
  130. console.log(`进行充值申请`)
  131. cardCzApplyAction().then((applyResult: any) => {
  132. //拿到订单 存起来
  133. state.orderNum = applyResult.orderNum
  134. setItem("orderNum", state.orderNum)
  135. //如果订单没有支付 走支付
  136. if (applyResult.orderStatus === 'ORDER_NOT_PAY') {
  137. //走支付
  138. console.log('走支付')
  139. wxPayment()
  140. }
  141. })
  142. }
  143. })
  144. } else {
  145. //走蓝牙进行修复
  146. uni.navigateTo({
  147. url: `/pages/bluetooth/device-active-step3?routeType=2`,
  148. });
  149. }
  150. })
  151. }
  152. /*读卡*/
  153. const getCardId = () => {
  154. console.log('======获取卡信息======')
  155. let cmdArr = [
  156. cmd.HOME_DIRECTORY,
  157. //选择主目
  158. cmd.APPLICATION_DIRECTORY,
  159. //选择文件1001--DF01联网收费应用目录
  160. cmd.CMD_READBINARY,
  161. //15文件--卡片发行基本数据文件
  162. cmd.CMD_GETBALANCE
  163. //钱包
  164. ];
  165. tools.showLoadingAlert("正在执行指令");
  166. bluetoothUtil.transCmd(cmdArr, "10", function(res) {
  167. tools.hideLoadingAlert();
  168. //10:写卡 20:写OBU
  169. let str = res[2].substring(res[2].length - 4, res[2].length);
  170. let str3 = res[3].substring(res[3].length - 4, res[3].length);
  171. if (str == "9000" || str3 == "9000") {
  172. if (res[2].length > 86 || res[3] >= 12) {
  173. card.cardId = res[2].substring(20, 40); //卡号
  174. card.startTime = res[2].substring(40, 48);
  175. card.endTime = res[2].substring(48, 56);
  176. card.version = res[2].substring(18, 19) >= 4 ? "4x" : "2x";
  177. card.netId = res[2].substring(20, 24);
  178. card.cardType = res[2].substring(28, 29) == 23 ? 1 : 2;
  179. card.vehiclePlateColor = parseInt(res[2].substring(82, 84), 16);
  180. card.money = parseInt(parseInt(res[3].substring(0, 8), 16), 10),
  181. console.log('======卡信息======', card)
  182. if (card.cardId !== state.cardId) {
  183. msg('设备卡信息与当前充值卡号不匹配,请核对卡号')
  184. return;
  185. }
  186. quanCheckActionTrue().then(val => {
  187. checkQuanCengEvent(val)
  188. })
  189. } else {
  190. console.error("CMD_READBINARY指令长度不符" + res[2])
  191. tools.hideLoadingAlert();
  192. }
  193. }
  194. console.error("CMD_READBINARY指令长度不符" + res[2])
  195. tools.hideLoadingAlert();
  196. });
  197. };
  198. const checkQuanCengEvent = (val: any) => {
  199. if (val.chargeStatus === 1) {
  200. let cmdArr = val.command.split(",");
  201. uni.showLoading({
  202. title: "写入中"
  203. })
  204. bluetoothUtil.transCmd(cmdArr, "10", function(res) {
  205. let response = res.toString();
  206. var dic = {
  207. command: val.command,
  208. cosResponse: response,
  209. }
  210. uni.hideLoading()
  211. cardCzXFCheckAction().then(xfRes => {
  212. quanApplyAction(dic).then(value => {
  213. console.log('圈层申请完后的结果')
  214. console.log(value)
  215. //圈存初始化验证通过 , 进行圈存
  216. if (value.commandType === 2) {
  217. uanConfirmAction(value).then(
  218. confirmResult => {
  219. msg('充值成功')
  220. })
  221. } else {
  222. msg('圈存初始化指令验证失败, 重新初始化')
  223. }
  224. })
  225. })
  226. });
  227. } else {
  228. let cmdArr = val.command.split(",");
  229. uni.showLoading({
  230. title: "写入中"
  231. })
  232. bluetoothUtil.transCmd(cmdArr, "10", function(res) {
  233. var status = res[1].substring(res[1].length - 4,
  234. res[1].length);
  235. console.log('打印状态')
  236. console.log(status)
  237. if (status === '9000') {
  238. console.log('修复指令入参')
  239. let response = res.toString();
  240. var dic = {
  241. command: val.command,
  242. cosResponse: response,
  243. rechargeId: val.rechargeId
  244. }
  245. quanFixAction(dic).then(value => {
  246. console.log(value)
  247. console.log('修复结果返回')
  248. var fixStatus = value.fixStatus;
  249. //圈存修复COS指令Response信息不足,重新进行修复初始化
  250. if (fixStatus === 3) {
  251. let xfcmdArr = value.command.split(",");
  252. bluetoothUtil.transCmd(xfcmdArr, "10", function(resValueData) {
  253. var status = resValueData[1].substring(resValueData[1].length -
  254. 4,
  255. resValueData[1].length);
  256. console.log('打印状态')
  257. console.log(status)
  258. if (status === '9000') {
  259. console.log(resValueData)
  260. var valueResponse = resValueData.toString();
  261. console.log(response)
  262. var applyDic = {
  263. command: value.command,
  264. cosResponse: valueResponse,
  265. rechargeId: value.rechargeId
  266. }
  267. console.log('消费成功')
  268. quanApplyAction(applyDic).then(
  269. applyValue => {
  270. uanConfirmAction(applyValue).then(
  271. confirmResult => {
  272. msg('充值成功')
  273. })
  274. })
  275. }
  276. })
  277. } else if (fixStatus === 2) {
  278. uanConfirmSucessAction(value).then(
  279. confirmResult => {
  280. msg('充值成功')
  281. })
  282. } else if (fixStatus === 1) {
  283. }
  284. })
  285. uni.hideLoading()
  286. }
  287. });
  288. }
  289. }
  290. /*透传*/
  291. const transCmd = (cmd) => {
  292. bluetoothUtil.transCmd(cmd, "10", function(res) {
  293. var status = res[1].substring(res[1].length -
  294. 4,
  295. res[1].length);
  296. if (status === '9000') {
  297. return res.toString()
  298. }
  299. })
  300. }
  301. /*圈层检测 -假数据校验*/
  302. const quanCheckAction = () => {
  303. var data = {
  304. cardId: state.cardId, //卡号
  305. fee: state.fee,
  306. preBalance: state.mockpreBalance, //假余额
  307. tradeType: 14,
  308. };
  309. const options = {
  310. type: 2,
  311. data: data,
  312. method: "POST",
  313. showLoading: true,
  314. };
  315. return new Promise(async (resolve, reject) => {
  316. const res = await request(quanCheck, options);
  317. const data = stringToJson(res.bizContent);
  318. resolve(data);
  319. }).catch((error) => {
  320. reject(error);
  321. });
  322. }
  323. const quanCheckActionTrue = () => {
  324. console.log('进行真实圈层检测')
  325. var data = {
  326. cardId: card.cardId,
  327. fee: state.fee,
  328. preBalance: card.money,
  329. tradeType: 14,
  330. };
  331. const options = {
  332. type: 2,
  333. data: data,
  334. method: "POST",
  335. showLoading: true,
  336. };
  337. return new Promise(async (resolve, reject) => {
  338. const res = await request(quanCheck, options);
  339. const data = stringToJson(res.bizContent);
  340. resolve(data);
  341. }).catch((error) => {
  342. reject(error);
  343. });
  344. }
  345. /*充值消费*/
  346. const cardCzXFCheckAction = () => {
  347. var data = {
  348. cardId: card.cardId, //修复初始化的指令
  349. openId: getItem(StorageKeys.OpenId), //修复初始化结果
  350. orderNum: state.orderNum, //充值流水号
  351. };
  352. const options = {
  353. type: 2,
  354. data: data,
  355. method: "POST",
  356. showLoading: true,
  357. };
  358. return new Promise(async (resolve, reject) => {
  359. const res = await request(cardCzXFCheck, options);
  360. const data = stringToJson(res.bizContent);
  361. resolve(data);
  362. }).catch((error) => {
  363. reject(error);
  364. });
  365. }
  366. /*圈存修复*/
  367. const quanFixAction = (val) => {
  368. var data = {
  369. command: val.command, //修复初始化的指令
  370. cosResponse: val.cosResponse, //修复初始化结果
  371. rechargeId: val.rechargeId, //充值流水号
  372. };
  373. const options = {
  374. type: 2,
  375. data: data,
  376. method: "POST",
  377. showLoading: true,
  378. };
  379. return new Promise(async (resolve, reject) => {
  380. const res = await request(quanXf, options);
  381. const data = stringToJson(res.bizContent);
  382. resolve(data);
  383. }).catch((error) => {
  384. reject(error);
  385. });
  386. }
  387. /*圈层申请*/
  388. const quanApplyAction = (data) => {
  389. var form = {
  390. cardId: card.cardId,
  391. fee: state.fee,
  392. preBalance: card.money,
  393. tradeType: 14,
  394. command: data.command,
  395. cosResponse: data.cosResponse,
  396. orderId: state.orderNum,
  397. rechargeId: data.rechargeId
  398. };
  399. const options = {
  400. type: 2,
  401. data: form,
  402. method: "POST",
  403. showLoading: true,
  404. };
  405. return new Promise(async (resolve, reject) => {
  406. const res = await request(quanApply, options);
  407. const data = stringToJson(res.bizContent);
  408. resolve(data);
  409. }).catch((error) => {
  410. reject(error);
  411. });
  412. }
  413. /*圈层确认*/
  414. const uanConfirmAction = (data) => {
  415. console.log('圈层确认进入')
  416. let cmdArr = data.command.split(",");
  417. console.log(cmdArr)
  418. bluetoothUtil.transCmd(cmdArr, "10", function(res) {
  419. console.log('圈层透传')
  420. console.log(res)
  421. var arraylenth = res.length
  422. var status = res[arraylenth - 1].substring(res[arraylenth - 1].length -
  423. 4,
  424. res[arraylenth - 1].length);
  425. console.log('打印圈层确认指令状态')
  426. console.log(status)
  427. if (status === '9000') {
  428. var form = {
  429. command: data.command,
  430. cosResponse: res.toString(),
  431. rechargeId: data.rechargeId,
  432. paidAmount: state.fee,
  433. giftAmount: 0,
  434. };
  435. const options = {
  436. type: 2,
  437. data: form,
  438. method: "POST",
  439. showLoading: true,
  440. };
  441. return new Promise(async (resolve, reject) => {
  442. const res = await request(quanConfirm, options);
  443. const data = stringToJson(res.bizContent);
  444. resolve(data);
  445. }).catch((error) => {
  446. reject(error);
  447. });
  448. }
  449. })
  450. }
  451. const uanConfirmSucessAction = (data) => {
  452. var form = {
  453. command: data.command,
  454. cosResponse: '9000',
  455. rechargeId: data.rechargeId,
  456. paidAmount: state.fee,
  457. giftAmount: 0,
  458. };
  459. const options = {
  460. type: 2,
  461. data: form,
  462. method: "POST",
  463. showLoading: true,
  464. };
  465. return new Promise(async (resolve, reject) => {
  466. const res = await request(quanConfirm, options);
  467. const data = stringToJson(res.bizContent);
  468. resolve(data);
  469. }).catch((error) => {
  470. reject(error);
  471. });
  472. }
  473. /*充值检测*/
  474. const czCheckAction = () => {
  475. var form = {
  476. cardId: state.cardId,
  477. openId: getItem(StorageKeys.OpenId),
  478. };
  479. const options = {
  480. type: 2,
  481. data: form,
  482. method: "POST",
  483. showLoading: true,
  484. };
  485. return new Promise(async (resolve, reject) => {
  486. const res = await request(cardCzPayResultCheck, options);
  487. const data = stringToJson(res.bizContent);
  488. resolve(data);
  489. }).catch((error) => {
  490. reject(error);
  491. });
  492. }
  493. /*充值申请*/
  494. const cardCzApplyAction = () => {
  495. var data = {
  496. cardId: state.cardId,
  497. openId: getItem(StorageKeys.OpenId),
  498. rechargeMoney: state.fee,
  499. };
  500. const options = {
  501. type: 2,
  502. data: data,
  503. method: "POST",
  504. showLoading: true,
  505. };
  506. return new Promise(async (resolve, reject) => {
  507. const res = await request(cardCzApply, options);
  508. const data = stringToJson(res.bizContent);
  509. resolve(data);
  510. }).catch((error) => {
  511. reject(error);
  512. });
  513. }
  514. //获取微信小程序openid
  515. const getOpenID = () => {
  516. uni.login({
  517. provider: "weixin",
  518. success: function(e) {
  519. wx.request({
  520. url: `https://api.weixin.qq.com/sns/jscode2session?appid=${wechatAppID}&secret=${wechatSecret}&js_code=${e.code}&grant_type=authorization_code`,
  521. success: (res: any) => {
  522. state.openid = res.data.openid;
  523. },
  524. });
  525. },
  526. fail: function() {
  527. msg('获取不到oppenId,请检查AppID和Secret是否争取')
  528. }
  529. });
  530. };
  531. //掉起微信支付
  532. const wxPayment = () => {
  533. // #ifdef MP-WEIXIN
  534. const options = {
  535. type: 2,
  536. data: {
  537. openId: getItem(StorageKeys.OpenId),
  538. wxOpenId: state.openid,
  539. cardId: state.cardId,
  540. orderNum: state.orderNum,
  541. payConfigId: wechatPayConfigId,
  542. body: '储值卡充值'
  543. },
  544. method: "POST",
  545. showLoading: true,
  546. };
  547. request(cardCzPay, options).then((res) => {
  548. const data = stringToJson(res.bizContent);
  549. uni.requestPayment({
  550. provider: "wxpay",
  551. orderInfo: "",
  552. timeStamp: data.timestamp,
  553. nonceStr: data.noncestr,
  554. package: data.wxPackage ?? "",
  555. signType: data.signType,
  556. paySign: data.sign,
  557. success: function() {
  558. //回调订单状态
  559. console.log('回调订单状态')
  560. checkOrder();
  561. },
  562. fail: function(err) {
  563. confirm(err, () => {}, "支付失败", false);
  564. },
  565. });
  566. });
  567. // #endif
  568. };
  569. //支付成功改变订单状态
  570. const checkOrder = () => {
  571. const options = {
  572. type: 2,
  573. data: {
  574. cardId: state.cardId,
  575. openId: getItem(StorageKeys.OpenId),
  576. orderNum: state.orderNum
  577. },
  578. method: "POST",
  579. showLoading: true,
  580. };
  581. request(cardCzPayResult, options).then((res) => {
  582. const data = stringToJson(res.bizContent);
  583. if (data.tradeState === 'SUCCESS')
  584. uni.navigateTo({
  585. url: `/pages/bluetooth/device-active-step3?routeType=2`,
  586. });
  587. console.log(data)
  588. });
  589. };
  590. </script>
  591. <style>
  592. .account {
  593. height: 224rpx;
  594. width: 690rpx;
  595. margin: 30rpx;
  596. position: relative;
  597. }
  598. .account .account-text {
  599. display: flex;
  600. justify-content: space-between;
  601. padding: 20rpx 50rpx;
  602. height: 100%;
  603. }
  604. .account .balance {
  605. display: flex;
  606. flex-direction: column;
  607. justify-content: center;
  608. align-items: flex-start;
  609. color: #ffffff;
  610. }
  611. .account .balance-tit {
  612. font-size: 26rpx;
  613. margin-bottom: 10rpx;
  614. }
  615. .account .balance-val {
  616. font-size: 56rpx;
  617. }
  618. .account .right-box {
  619. display: flex;
  620. flex-direction: column;
  621. justify-content: flex-start;
  622. align-items: flex-end;
  623. }
  624. .account .right-box .no {
  625. font-size: 26rpx;
  626. color: #ffffff;
  627. }
  628. .account .right-box .btn {
  629. font-size: 26rpx;
  630. padding: 0 20rpx;
  631. box-sizing: border-box;
  632. height: 42rpx;
  633. line-height: 42rpx;
  634. color: #28d20f;
  635. background: #ffffff;
  636. border-radius: 21rpx;
  637. display: inline-block;
  638. margin-top: 18rpx;
  639. }
  640. .account .head-bg {
  641. width: 690rpx;
  642. height: 224rpx;
  643. position: absolute;
  644. left: 0;
  645. top: 0;
  646. z-index: -99;
  647. }
  648. .input-box {
  649. display: flex;
  650. flex-direction: column;
  651. justify-content: center;
  652. align-items: center;
  653. padding: 20rpx 0;
  654. }
  655. .input-box .input {
  656. width: 430rpx;
  657. height: 98rpx;
  658. line-height: 98rpx;
  659. background: #f8f8f8;
  660. border: 1px solid #999999;
  661. border-radius: 6rpx;
  662. font-size: 30rpx;
  663. text-align: center;
  664. }
  665. .input-box .tips {
  666. color: #666666;
  667. font-size: 26rpx;
  668. margin-top: 18rpx;
  669. }
  670. .tabs {}
  671. .tabs .tab-tit {
  672. font-size: 30rpx;
  673. color: #333;
  674. padding: 45rpx 0 22rpx 30rpx;
  675. }
  676. .tabs .tab {
  677. display: flex;
  678. flex-wrap: wrap;
  679. justify-content: space-evenly;
  680. }
  681. .tabs .tab .item {
  682. width: 210rpx;
  683. height: 100rpx;
  684. background: #f6fff7;
  685. border: 1px solid #dcdde1;
  686. box-sizing: border-box;
  687. line-height: 100rpx;
  688. border-radius: 6rpx;
  689. text-align: center;
  690. margin-bottom: 24rpx;
  691. color: #333333;
  692. font-size: 32rpx;
  693. }
  694. .tabs .tab .item.active {
  695. border: 1px solid #24cc49;
  696. color: #24cc49;
  697. }
  698. .btn-primary {
  699. width: 670rpx;
  700. height: 80rpx;
  701. line-height: 80rpx;
  702. background: linear-gradient(-90deg, #43a1e0 0%, #13e7c1 100%);
  703. border-radius: 40rpx;
  704. font-size: 32rpx;
  705. color: #ffffff;
  706. margin: 200rpx 40rpx 0;
  707. text-align: center;
  708. }
  709. </style>