index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  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.discountAmtStart)"
  66. >
  67. <span>¥</span>
  68. <span>{{ item.discountAmtStart }}</span>
  69. <span>满{{ item.discountAmtStart }}送{{ item.presentAmt }}{{item.settingRuleType=="1"?"元":"%"}}</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 .charge {
  406. height: 7.5rem;
  407. background: #f2f2f2;
  408. border-radius: 0.42rem 0.42rem 0rem 0rem;
  409. box-sizing: border-box;
  410. padding: 0.3rem 0;
  411. display: flex;
  412. flex-direction: column;
  413. align-items: center;
  414. }
  415. .charge-index .mask .charge > div:not(:nth-child(1)):not(.container) {
  416. margin-top: 0.2rem;
  417. }
  418. .charge-index .mask .charge .title {
  419. width: 6.5rem;
  420. height: 0.45rem;
  421. font-size: 0.32rem;
  422. font-family: PingFangSC-Regular, PingFang SC;
  423. font-weight: 400;
  424. color: #111111;
  425. line-height: 0.45rem;
  426. text-align: center;
  427. }
  428. .charge-index .mask .charge .title .close {
  429. background: url("~static/personal/6_d05_close@2x.png") no-repeat 0px 0px;
  430. background-size: 100% 100%;
  431. width: 0.4rem;
  432. height: 0.4rem;
  433. float: right;
  434. }
  435. .charge-index .mask .charge .tip {
  436. height: 0.33rem;
  437. font-size: 0.24rem;
  438. font-family: PingFangSC-Regular, PingFang SC;
  439. font-weight: 400;
  440. color: #aaaaaa;
  441. line-height: 0.33rem;
  442. }
  443. .charge-index .mask .charge .amount {
  444. width: 6.9rem;
  445. height: 1.2rem;
  446. background: url("~static/personal/biankuang@2x.png") no-repeat 0px 0px;
  447. background-size: 100% 100%;
  448. position: relative;
  449. }
  450. .charge-index .mask .charge .amount span:nth-child(1) {
  451. height: 0.4rem;
  452. font-size: 0.28rem;
  453. font-weight: 600;
  454. color: #884e16;
  455. line-height: 0.4rem;
  456. position: absolute;
  457. top: 0.4rem;
  458. left: 0.51rem;
  459. }
  460. .charge-index .mask .charge .amount input {
  461. background: none;
  462. outline: none;
  463. border: none;
  464. height: 0.4rem;
  465. font-size: 0.28rem;
  466. font-family: PingFangSC-Regular, PingFang SC;
  467. font-weight: 400;
  468. color: #111111;
  469. line-height: 0.4rem;
  470. position: absolute;
  471. top: 0.4rem;
  472. left: 2.54rem;
  473. }
  474. .charge-index .mask .charge .amount span:nth-last-child(1) {
  475. height: 0.4rem;
  476. font-size: 0.28rem;
  477. font-family: PingFangSC-Regular, PingFang SC;
  478. font-weight: 400;
  479. color: #aaaaaa;
  480. line-height: 0.4rem;
  481. position: absolute;
  482. top: 0.4rem;
  483. right: 0.4rem;
  484. }
  485. .charge-index .mask button {
  486. border: none;
  487. outline: none;
  488. bottom: 0.7rem;
  489. position: absolute;
  490. width: 6.9rem;
  491. height: 0.8rem;
  492. left: 0.3rem;
  493. background: #cccccc;
  494. border-radius: 0.45rem;
  495. font-size: 0.32rem;
  496. font-family: PingFangSC-Regular, PingFang SC;
  497. font-weight: 400;
  498. color: #ffffff;
  499. line-height: 0.45rem;
  500. }
  501. .charge-index .mask > button.selected {
  502. background-color: #0ea374;
  503. }
  504. .charge-index .mask .container {
  505. margin-top: 0.4rem;
  506. display: flex;
  507. flex-wrap: wrap;
  508. padding: 0.3rem 0.3rem;
  509. justify-content: space-between;
  510. overflow: scroll;
  511. height: 2rem;
  512. }
  513. .charge-index .mask .container .btn {
  514. display: inline-block;
  515. width: 3.3rem;
  516. height: 0.84rem;
  517. background: #ffffff;
  518. box-shadow: 0 0.04rem 0.08rem 0 #f2f2f2;
  519. border-radius: 0.42rem;
  520. margin-bottom: 0.35rem;
  521. position: relative;
  522. box-sizing: border-box;
  523. padding: 0 0.26rem;
  524. text-align: center;
  525. }
  526. .charge-index .mask .container .btn:nth-child(odd) {
  527. margin-right: 0.3rem;
  528. }
  529. .charge-index .mask .container .btn span {
  530. font-size: 0.35rem;
  531. font-family: PingFangSC-Regular, PingFang SC;
  532. color: #895017;
  533. line-height: 0.84rem;
  534. }
  535. .charge-index .mask .container .btn span:nth-child(1) {
  536. border-radius: 0.42rem;
  537. font-size: 0.2rem;
  538. float: left;
  539. }
  540. .charge-index .mask .container .btn span:nth-child(2) {
  541. float: left;
  542. font-weight: 600;
  543. }
  544. .charge-index .mask .container .btn span:nth-child(3) {
  545. font-size: 0.2rem;
  546. font-weight: 400;
  547. color: rgba(0, 0, 0, 0.29);
  548. float: right;
  549. }
  550. </style>