index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. <template>
  2. <div class="charge-index">
  3. <div class="background">
  4. <div class="headline"></div>
  5. </div>
  6. <div class="content">
  7. <div class="title">我的卡片 ({{ cardList.length }})</div>
  8. <div
  9. class="gasoline"
  10. v-for="item in cardList"
  11. :key="item.id"
  12. :class="[
  13. item.cardOilsType == 1
  14. ? 'gasoline'
  15. : item.cardOilsType == 2
  16. ? 'diesel'
  17. : '',
  18. ]"
  19. >
  20. <div class="title">
  21. {{
  22. item.cardOilsType == 1
  23. ? "汽油卡"
  24. : item.cardOilsType == 2
  25. ? "柴油卡"
  26. : ""
  27. }}
  28. </div>
  29. <div class="text">
  30. 余额 <span>{{ item.amt.toFixed(2) }}</span
  31. >元
  32. </div>
  33. <button @click="goCharge(item)">充值</button>
  34. </div>
  35. </div>
  36. <div class="mask" :class="[mask ? '' : 'hidden']">
  37. <div class="layer" @click="switchMask"></div>
  38. <div class="charge">
  39. <div class="title">
  40. 充值
  41. <div class="close" @click="switchMask"></div>
  42. </div>
  43. <div class="tip">提示:充值汽油1000元,赠送100元,实际到账1100元</div>
  44. <div class="amount">
  45. <span>{{
  46. chargeItem.cardOilsType == 1
  47. ? "汽油充值"
  48. : chargeItem.cardOilsType == 2
  49. ? "柴油充值"
  50. : ""
  51. }}</span>
  52. <input
  53. placeholder="输入充值金额"
  54. v-model="chargeAmount"
  55. type="number"
  56. step="0.01"
  57. min="0"
  58. onkeyup="this.value= this.value.match(/\d+(\.\d{0,2})?/) ? this.value.match(/\d+(\.\d{0,2})?/)[0] : ''"
  59. />
  60. <span>¥</span>
  61. </div>
  62. </div>
  63. <van-button
  64. :class="[chargeAmount !== '' ? 'selected' : '']"
  65. :loading="payLoading"
  66. loading-text="拉取支付中..."
  67. @click="charge"
  68. :disabled="chargeAmount === ''"
  69. >
  70. 确认充值
  71. </van-button>
  72. </div>
  73. </div>
  74. </template>
  75. <script>
  76. import ProductList from "../../components/ProductList";
  77. import { mapState, mapGetters, mapActions } from "vuex";
  78. import wx from "weixin-js-sdk";
  79. import Vue from "vue";
  80. import { Toast, Button } from "vant";
  81. Vue.use(Toast);
  82. Vue.use(Button);
  83. export default {
  84. head() {
  85. return {
  86. title: "会员充值",
  87. };
  88. },
  89. data() {
  90. return {
  91. mask: false,
  92. cardList: [],
  93. chargeItem: {},
  94. chargeAmount: "",
  95. stationInfo: {},
  96. payLoading: false,
  97. };
  98. },
  99. components: {
  100. ProductList,
  101. },
  102. computed: {
  103. ...mapState({
  104. pointInfo: (state) => state.point.pointInfo,
  105. }),
  106. ...mapGetters({
  107. userInfo: "authen/userInfo",
  108. }),
  109. },
  110. created() {
  111. this.init();
  112. },
  113. methods: {
  114. ...mapActions({
  115. getSdkConfig: "authen/getSdkConfig",
  116. }),
  117. switchMask() {
  118. this.mask = !this.mask;
  119. },
  120. goCharge(ele) {
  121. this.chargeItem = ele;
  122. this.switchMask();
  123. },
  124. charge() {
  125. this.chargeAmount = this.chargeAmount.replace(/\.$/g, "");
  126. this.pay();
  127. },
  128. init() {
  129. // 获取卡片
  130. this.$axios
  131. .$get("/getElectronicCardList", {
  132. params: {
  133. unionId: this.unionId,
  134. stationId: this.stationId,
  135. },
  136. })
  137. .then((res) => {
  138. if (res.retCode === 0) {
  139. this.cardList = res.data;
  140. }
  141. });
  142. // 这一块纯为了后端方便而已,获取station的信息
  143. this.$axios
  144. .$get("/getStationInfo", {
  145. params: {
  146. stationId: this.stationId,
  147. },
  148. })
  149. .then((res) => {
  150. this.stationInfo = res.data;
  151. });
  152. },
  153. async pay() {
  154. try {
  155. this.payLoading = true;
  156. const orderData = await this.$axios.$post("/rechargeBalance", {
  157. amt: this.chargeAmount,
  158. stationId: this.stationId,
  159. customerName: this.userInfo.nickname,
  160. cardOilsType: this.chargeItem.cardOilsType,
  161. payType: "1",
  162. unionId: this.unionId,
  163. stationName: this.stationInfo.stationName,
  164. });
  165. if (orderData.retCode !== 0) {
  166. throw new Error("生成订单失败");
  167. }
  168. const orderNum = orderData.data;
  169. const subject =
  170. this.stationInfo.stationName +
  171. "_" +
  172. (this.chargeItem.cardOilsType == 1
  173. ? "汽油电子卡充值"
  174. : this.chargeItem.cardOilsType == 2
  175. ? "柴油电子卡充值"
  176. : "");
  177. console.log(subject);
  178. const payInfoData = await this.$axios.$post("/rechargeBalanceSXFPay", {
  179. amt: this.chargeAmount,
  180. openId: this.openId,
  181. stationId: this.stationId,
  182. userType: "1",
  183. subject,
  184. orderNo: orderNum,
  185. });
  186. if (payInfoData.retCode !== 0) {
  187. throw new Error("支付参数拉取失败");
  188. }
  189. if (payInfoData.data.code !== "0000") {
  190. throw new Error(payInfoData.data.msg);
  191. }
  192. const timestamp = payInfoData.data.respData.payTimeStamp;
  193. const nonceStr = payInfoData.data.respData.paynonceStr;
  194. const packageOrg = payInfoData.data.respData.payPackage;
  195. const signType = payInfoData.data.respData.paySignType;
  196. const paySign = payInfoData.data.respData.paySign;
  197. const that = this;
  198. // 拉取微信支付
  199. this.getSdkConfig([
  200. "chooseWXPay",
  201. ]).then((res) => {
  202. wx.config(res);
  203. wx.ready(function (res) {
  204. wx.chooseWXPay({
  205. timestamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
  206. nonceStr, // 支付签名随机串,不长于 32 位
  207. package: packageOrg, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
  208. signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
  209. paySign, // 支付签名
  210. success: function (res) {
  211. // that.$router.push("/personal/charge");
  212. that.payLoading = false;
  213. alert("支付完成");
  214. },
  215. cancel: function (err) {
  216. that.payLoading = false;
  217. alert("您取消了付款");
  218. },
  219. fail: function (err) {
  220. that.payLoading = false;
  221. alert(err);
  222. },
  223. });
  224. });
  225. wx.error(function (res) {
  226. alert("拉取微信Sdk配置出错");
  227. });
  228. });
  229. } catch (err) {
  230. this.payLoading = false;
  231. alert(err);
  232. }
  233. },
  234. },
  235. };
  236. </script>
  237. <style>
  238. .charge-index {
  239. position: relative;
  240. height: 100vh;
  241. }
  242. .charge-index .background {
  243. background-color: #f3b335;
  244. width: 7.5rem;
  245. height: 7.5rem;
  246. position: relative;
  247. background: url("~static/personal/23@2x.png") no-repeat 0px 0px;
  248. background-size: 100% 100%;
  249. z-index: -1;
  250. overflow: hidden;
  251. }
  252. .charge-index .background .headline {
  253. width: 6.9rem;
  254. height: 4.33rem;
  255. background: url("~static/personal/bj2@2x.png") no-repeat 0px 0px;
  256. background-size: 100% 100%;
  257. margin: 0.3rem auto;
  258. }
  259. .charge-index .content {
  260. position: absolute;
  261. top: 3.78rem;
  262. bottom: 0rem;
  263. left: 0rem;
  264. right: 0rem;
  265. background-color: #ffffff;
  266. border-radius: 0.3rem 0.3rem 0 0;
  267. padding: 0.3rem;
  268. display: flex;
  269. flex-direction: column;
  270. }
  271. .charge-index .content > div:not(.title) {
  272. width: 6.9rem;
  273. height: 1.6rem;
  274. margin-top: 0.3rem;
  275. position: relative;
  276. }
  277. .charge-index .content .title {
  278. font-size: 0.35rem;
  279. color: #333333;
  280. font-weight: 600;
  281. }
  282. .charge-index .content .gasoline {
  283. background: url("~static/personal/bj3@2x.png") no-repeat 0px 0px;
  284. background-size: 100% 100%;
  285. }
  286. .charge-index .content .diesel {
  287. background: url("~static/personal/bj4@2x.png") no-repeat 0px 0px;
  288. background-size: 100% 100%;
  289. }
  290. .charge-index .content div .title {
  291. height: 0.45rem;
  292. font-size: 0.32rem;
  293. font-family: PingFangSC-Medium, PingFang SC;
  294. font-weight: 500;
  295. color: #ffffff;
  296. line-height: 0.45rem;
  297. position: absolute;
  298. top: 0.27rem;
  299. left: 1.67rem;
  300. }
  301. .charge-index .content .gasoline .text {
  302. height: 0.45rem;
  303. font-size: 0.32rem;
  304. font-family: PingFangSC-Medium, PingFang SC;
  305. font-weight: 500;
  306. color: #3db58f;
  307. line-height: 0.45rem;
  308. position: absolute;
  309. top: 0.9rem;
  310. left: 1.67rem;
  311. }
  312. .charge-index .content .diesel .text {
  313. height: 0.45rem;
  314. font-size: 0.32rem;
  315. font-family: PingFangSC-Medium, PingFang SC;
  316. font-weight: 500;
  317. color: #ea8c7d;
  318. line-height: 0.45rem;
  319. position: absolute;
  320. top: 0.9rem;
  321. left: 1.67rem;
  322. }
  323. .charge-index .content div .text span {
  324. color: #fff;
  325. }
  326. .charge-index .content div button {
  327. border: none;
  328. background-color: transparent;
  329. outline: none;
  330. display: block;
  331. width: 1.4rem;
  332. height: 0.6rem;
  333. background: #ffffff;
  334. border-radius: 0.3rem;
  335. position: absolute;
  336. top: 0.5rem;
  337. right: 0.3rem;
  338. }
  339. .charge-index .content .gasoline button {
  340. color: #24ac81;
  341. }
  342. .charge-index .content .diesel button {
  343. color: #e87d6d;
  344. }
  345. .charge-index .mask {
  346. position: fixed;
  347. height: 100%;
  348. bottom: 0;
  349. left: 0;
  350. right: 0;
  351. transition: all 2s linear 2s;
  352. }
  353. .charge-index .hidden {
  354. display: none;
  355. }
  356. .charge-index .mask .layer {
  357. position: absolute;
  358. top: 0;
  359. bottom: 0;
  360. left: 0;
  361. right: 0;
  362. background: #111111;
  363. opacity: 0.59;
  364. }
  365. .charge-index .mask .charge {
  366. position: absolute;
  367. bottom: 0;
  368. left: 0;
  369. right: 0;
  370. height: 7.5rem;
  371. background: #f2f2f2;
  372. border-radius: 0.42rem 0.42rem 0rem 0rem;
  373. box-sizing: border-box;
  374. padding: 0.3rem 0;
  375. display: flex;
  376. flex-direction: column;
  377. align-items: center;
  378. transition: all 2s linear;
  379. }
  380. /*
  381. .charge-index .hidden .charge{
  382. height: 0;
  383. }
  384. */
  385. .charge-index .mask .charge div:not(:nth-child(1)) {
  386. margin-top: 0.2rem;
  387. }
  388. .charge-index .mask .charge .title {
  389. width: 6.5rem;
  390. height: 0.45rem;
  391. font-size: 0.32rem;
  392. font-family: PingFangSC-Regular, PingFang SC;
  393. font-weight: 400;
  394. color: #111111;
  395. line-height: 0.45rem;
  396. text-align: center;
  397. }
  398. .charge-index .mask .charge .title .close {
  399. background: url("~static/personal/6_d05_close@2x.png") no-repeat 0px 0px;
  400. background-size: 100% 100%;
  401. width: 0.4rem;
  402. height: 0.4rem;
  403. float: right;
  404. }
  405. .charge-index .mask .charge .tip {
  406. height: 0.33rem;
  407. font-size: 0.24rem;
  408. font-family: PingFangSC-Regular, PingFang SC;
  409. font-weight: 400;
  410. color: #aaaaaa;
  411. line-height: 0.33rem;
  412. }
  413. .charge-index .mask .charge .amount {
  414. width: 6.9rem;
  415. height: 1.2rem;
  416. background: url("~static/personal/biankuang@2x.png") no-repeat 0px 0px;
  417. background-size: 100% 100%;
  418. position: relative;
  419. }
  420. .charge-index .mask .charge .amount span:nth-child(1) {
  421. height: 0.4rem;
  422. font-size: 0.28rem;
  423. font-weight: 600;
  424. color: #884e16;
  425. line-height: 0.4rem;
  426. position: absolute;
  427. top: 0.4rem;
  428. left: 0.51rem;
  429. }
  430. .charge-index .mask .charge .amount input {
  431. background: none;
  432. outline: none;
  433. border: none;
  434. height: 0.4rem;
  435. font-size: 0.28rem;
  436. font-family: PingFangSC-Regular, PingFang SC;
  437. font-weight: 400;
  438. color: #111111;
  439. line-height: 0.4rem;
  440. position: absolute;
  441. top: 0.4rem;
  442. left: 2.54rem;
  443. }
  444. .charge-index .mask .charge .amount span:nth-last-child(1) {
  445. height: 0.4rem;
  446. font-size: 0.28rem;
  447. font-family: PingFangSC-Regular, PingFang SC;
  448. font-weight: 400;
  449. color: #aaaaaa;
  450. line-height: 0.4rem;
  451. position: absolute;
  452. top: 0.4rem;
  453. right: 0.4rem;
  454. }
  455. .charge-index .mask button {
  456. border: none;
  457. outline: none;
  458. bottom: 0.83rem;
  459. position: absolute;
  460. width: 6.9rem;
  461. height: 0.8rem;
  462. left: 0.3rem;
  463. background: #cccccc;
  464. border-radius: 0.45rem;
  465. font-size: 0.32rem;
  466. font-family: PingFangSC-Regular, PingFang SC;
  467. font-weight: 400;
  468. color: #ffffff;
  469. line-height: 0.45rem;
  470. }
  471. .charge-index .mask button.selected {
  472. background-color: #0ea374;
  473. }
  474. </style>