index.vue 14 KB

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