parkingFeeMsg.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. <template>
  2. <div :class="[theme, 'bg']">
  3. <!-- 领取临牌:成功 -->
  4. <!-- <div @click="qrCodesRule('8b0571a1')">test</div> -->
  5. <div class="success-box" v-if="type === 'success'">
  6. <img class="icon" :src="require(`@/pages/parkingFee/static/images/unlicensed/success.png`)" />
  7. <div class="status-title">临牌领取成功</div>
  8. <div class="status-info">若未正常抬杠,请关闭当前页面重新扫码</div>
  9. <div class="status-info">或联系车场管理人员处理</div>
  10. <div class="card-box">
  11. <img :src="`${require(`@/pages/parkingFee/static/images/unlicensed-0.png`)}`" style="margin-left: 10px"/>
  12. <div class="car-number">
  13. <div class="number">{{ vehicleNo | formatCarno}}</div>
  14. <div class="tips">车辆类型:无牌车辆</div>
  15. </div>
  16. </div>
  17. <k-button title="进入首页" disabledColor="#D1D2D9" @click="goHome" />
  18. </div>
  19. <!-- 领取临牌:失败 -->
  20. <div class="fail-box" v-if="type === 'fail'">
  21. <img class="icon" :src="require(`@/pages/parkingFee/static/images/unlicensed/fail.png`)" />
  22. <div class="status-title">临牌领取失败</div>
  23. <div class="status-info">请关闭当前页面重新扫码</div>
  24. <div class="status-info">或联系车场管理人员处理</div>
  25. <div class="card-box">
  26. <img :src="`${require(`@/pages/parkingFee/static/images/unlicensed-0.png`)}`" style="margin-left: 10px"/>
  27. <div class="car-number">
  28. <div class="number">临K ???</div>
  29. <div class="tips">车辆类型:无牌车辆</div>
  30. </div>
  31. </div>
  32. <k-button disabledColor="#D1D2D9" @click="scanCarCode">
  33. <template v-slot:left>
  34. <img class="unlicensed-scan" :src="`${require(`@/pages/parkingFee/static/images/unlicensed-scan.png`)}`" />
  35. </template>
  36. 重新扫码
  37. </k-button>
  38. </div>
  39. <!-- 支付成功 -->
  40. <div class="pay-box" v-if="type === 'pay'">
  41. <img class="pay-log" :src="require(`@/pages/parkingFee/static/images/unlicensed/pay.svg`)" />
  42. <span class="info">您已缴费成功,欢迎下次光临</span>
  43. </div>
  44. <!-- 出场临牌:失败 -->
  45. <div class="fail-box" v-if="type === 'outFail'">
  46. <img class="out-fail-log" :src="require(`@/pages/parkingFee/static/images/unlicensed/no-car-out.svg`)" />
  47. <k-button disabledColor="#D1D2D9" @click="scanCarCode">
  48. <template v-slot:left>
  49. <img class="unlicensed-scan" :src="`${require(`@/pages/parkingFee/static/images/unlicensed-scan.png`)}`" />
  50. </template>
  51. 重新扫码
  52. </k-button>
  53. <!-- <span class="info">您已缴费成功,欢迎下次光临</span>-->
  54. </div>
  55. </div>
  56. </template>
  57. <script>
  58. import { qrCodes,unlicensedCarCheckIn } from "@/api/parking";
  59. import baseMixins from "@/pages/parkingFee/mixins/base";
  60. import uni from '@/utils/uniHooks';
  61. import { initWxJsSdkConfig } from '@/utils/login';
  62. import { getPlatform,getUrlParams, theCommunicationBetweenWechatAndH5IsNormal, extractValuesFromString } from '@/utils';
  63. import {mapState} from "vuex";
  64. export default {
  65. mixins: [baseMixins],
  66. name: 'parkingFeeMsg',
  67. data() {
  68. return {
  69. type: '',
  70. vehicleNo: '',
  71. };
  72. },
  73. computed:{
  74. ...mapState({
  75. unlicensedInfo: (state) => state.unlicensedInfo,
  76. isLogin: (state) => state.isLogin,
  77. })
  78. },
  79. async created() {
  80. // setTimeout(() => {
  81. // window?.toWXSendMsg({
  82. // type: 'nowRoute',
  83. // options: {
  84. // fullPath: '1'
  85. // },
  86. // });
  87. // }, 100);
  88. // setTimeout(() => {
  89. // uni.setNavigationBarTitle({
  90. // title: '停车缴费提示',
  91. // });
  92. // }, 300);
  93. // const platform = getPlatform();
  94. // console.log(this.$route.query);
  95. this.pageInit()
  96. try {
  97. // await initWxJsSdkConfig(['checkJsApi', 'scanQRCode']);
  98. } catch (e) {
  99. console.log(e)
  100. }
  101. },
  102. mounted() {},
  103. methods: {
  104. pageInit(){
  105. if (this.$route.query?.type) {
  106. this.type = this.$route.query?.type;
  107. }
  108. if (this.type === 'success') {
  109. this.vehicleNo = this.$route.query?.vehicleNo;
  110. }
  111. this.$store.commit('SET_UNLICENSED_INFO', {});
  112. },
  113. getParam(url, key) {
  114. let u = url.split('?')[1]
  115. let l = u.split('&')
  116. let o = {}
  117. l.forEach(i => {
  118. let key = i.split('=')[0]
  119. let value = i.split('=')[1]
  120. if (key == 'type') {
  121. if (value != 'test') {
  122. o[key] = value
  123. }
  124. } else {
  125. o[key] = value
  126. }
  127. })
  128. return o[key]
  129. },
  130. get_sa_utm(options) {
  131. return new Promise((resolve) => {
  132. try {
  133. let temp = ''
  134. // #ifdef MP-WEIXIN
  135. if (options.q) {
  136. temp = decodeURIComponent(options.q)
  137. }
  138. // #endif
  139. // #ifdef MP-ALIPAY
  140. // 如果是支付宝这边已经解析过了,则返回false,不做二次解析
  141. // const query = CacheTool.getMiniAppOptionsQuery()
  142. console.log('options.options::::', options.options)
  143. if (options.options.qrCode) {
  144. temp = decodeURIComponent(options.options.qrCode)
  145. }
  146. // #endif
  147. // temp = https://crm.kerryplus.com/t-parking?sa_utm=iE
  148. if (temp && temp.indexOf('?') > -1) {
  149. const sa_utm = this.getParam(temp, 'sa_utm');
  150. console.log('utm参数', sa_utm)
  151. return resolve(sa_utm || false)
  152. }
  153. console.log('二码合一参数解析失败', temp);
  154. return resolve(false)
  155. } catch (err) {
  156. resolve(false)
  157. }
  158. })
  159. },
  160. asyncRequest(url, data, method = 'GET') {
  161. return new Promise((resolve, reject) => {
  162. uni.request({
  163. url: url,
  164. data: data,
  165. header: JSON.parse(uni.getStorageSync('handleUser') || "{}"),
  166. method: method,
  167. success: (res) => {
  168. resolve(res.data);
  169. },
  170. fail: (err) => {
  171. reject(err);
  172. }
  173. });
  174. });
  175. },
  176. // 在异步函数中调用异步请求
  177. fetchData(id) {
  178. return new Promise(async (resolve, reject) => {
  179. try {
  180. const res = await this.asyncRequest(`${window.QR_CODE_BASE_URL}/c/${id}`);
  181. console.log('baseURLQrCode:::', res)
  182. // console.log('baseURLQrCodebaseURLQrCode::', res.data);
  183. if (res?.code == 200) {
  184. // console.log('77777777', res, queryStringToObject(res.data.url));
  185. // const [baseURL, key, value] = res.data.url.match(/(.*)=(.*)/)
  186. if (res?.data?.url) {
  187. // resolve({ key, value })
  188. const insideUrl =
  189. `/pages/package-parkingFee/parkingFeeWebViewLogin?${res.data.url}`
  190. // resolve(queryStringToObject(`t-page?${res.data.url}`))
  191. // #ifdef MP-ALIPAY
  192. my.navigateTo({
  193. url: `/pages/package-parkingFee/parkingFeeWebViewLogin?${res.data.url}`,
  194. complete: function (res) {
  195. console.log('回调::', res)
  196. },
  197. success: function (res) {
  198. console.log('跳转成功::', res)
  199. },
  200. fail: function (err) {
  201. console.log('跳转失败::', res)
  202. }
  203. });
  204. // #endif
  205. } else {
  206. reject(false)
  207. }
  208. }
  209. } catch (error) {
  210. console.error('请求失败', error);
  211. }
  212. })
  213. },
  214. async getqrcode (options) {
  215. try {
  216. const sa_utm = await this.get_sa_utm(options);
  217. if (sa_utm) {
  218. const params = await this.fetchData(sa_utm)
  219. console.log('二码合一参数::::', params)
  220. }
  221. } catch(err) {
  222. }
  223. },
  224. // 无牌车闸机扫码
  225. async scanCarCode() {
  226. var _this = this
  227. const runScanFn = (res) => {
  228. /*
  229. 针对微信的小程序码进行的兼容改造
  230. 微信扫码结束之后的返回参数 {"errMsg": "scanCode:ok", "scanType": "WX_CODE", "charSet": "ISO8859-1", "rawData": "bGsoP3gyT1Aud3QpbW1JeHRfVHJsUjg4JnR5cGU9dW5saWNlbnNlZElu", "path": "pages/automatic/automaticIndex?scene=code%3D9988%26type%3DunlicensedIn"}
  231. */
  232. if(res.scanType && res.scanType === 'WX_CODE' && res?.path) {
  233. const params = getUrlParams(`?${decodeURIComponent(res.path.replace(/.*scene=/g, ''))}`)
  234. this.$store.commit('SET_UNLICENSED_INFO', params);
  235. this.$nextTick(() => {
  236. this.qrCodesRule(params.code);
  237. })
  238. }
  239. // 兜底逻辑,如果是其他小程序扫描,则提取rawData,进行解析提取入参
  240. if(res.scanType && res.scanType === 'WX_CODE' && !res?.path){
  241. let path = atob(res.rawData)
  242. path = path.replace(/.*([a-z0-9]{6}&type)/g, '8b$1')
  243. const regex = /(\w+)&type=(\w+)/;
  244. const match = path.match(regex);
  245. const obj = { code: match[1], type: match[2] };
  246. this.$store.commit('SET_UNLICENSED_INFO', obj);
  247. this.$nextTick(() => {
  248. this.qrCodesRule(obj.code);
  249. })
  250. }
  251. };
  252. // 微信小程序
  253. // const platform = getPlatform();
  254. if (this.isAlipayClient) {
  255. // 判断微信小程序与 h5 是否正常通信
  256. // const isReload = await theCommunicationBetweenWechatAndH5IsNormal()
  257. // if(!isReload) {
  258. // uni.setStorageSync('isReload', 1)
  259. // window.location.reload()
  260. // return
  261. // }
  262. // 调用支付宝扫一扫功能
  263. window.toWXSendMsg({
  264. type: 'scanQRCode',
  265. });
  266. console.log('二码合一参数::::')
  267. // runScanFn({type: "scanQRCodeOver", options: {imageChannel: "camera", qrCode: "https://crm.kerryplus.com/t-parking?sa_utm=mJ", rawData: "aHR0cHM6Ly9jcm0ua2VycnlwbHVzLmNvbS90LXBhcmtpbmc/c2FfdXRtPW1K", result: "https://crm.kerryplus.com/t-parking?sa_utm=mJ", scanType: "QR", errMsg: "scanCode:ok"}});
  268. // TODO 兼容支付宝无牌车扫码
  269. // this.getqrcode({type: "scanQRCodeOver", options: {imageChannel: "camera", qrCode: "https://crm.kerryplus.com/t-parking?sa_utm=mJ", rawData: "aHR0cHM6Ly9jcm0ua2VycnlwbHVzLmNvbS90LXBhcmtpbmc/c2FfdXRtPW1K", result: "https://crm.kerryplus.com/t-parking?sa_utm=mJ", scanType: "QR", errMsg: "scanCode:ok"}})
  270. window.subscribe('scanQRCodeOver', (options) => {
  271. console.log('微信扫码结束之后的返回参数', {type: "scanQRCodeOver", options: {imageChannel: "camera", qrCode: "https://crm.kerryplus.com/t-parking?sa_utm=mJ", rawData: "aHR0cHM6Ly9jcm0ua2VycnlwbHVzLmNvbS90LXBhcmtpbmc/c2FfdXRtPW1K", result: "https://crm.kerryplus.com/t-parking?sa_utm=mJ", scanType: "QR", errMsg: "scanCode:ok"}});
  272. // const sa_utm = await this.get_sa_utm(options);
  273. // console.log('sa_utm:::', sa_utm)
  274. _this.getqrcode(options)
  275. // runScanFn(options);
  276. });
  277. } else {
  278. try {
  279. this.$wx.scanQRCode({
  280. needResult: 0, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
  281. // onlyFromCamera: false,
  282. // desc: 'scanQRCode desc',
  283. // needResult: 0, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
  284. // scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有
  285. success: (res) => {
  286. console.log('H5页面扫码获取到的参数——成功res2', res);
  287. // let path = res.resultStr.replace(/.*([a-z0-9]{6}&type)/g, '8b$1')
  288. // const regex = /(\w+)&type=(\w+)/;
  289. // const match = path.match(regex);
  290. // const obj = {code: match[1], type: match[2]};
  291. // this.$store.commit('SET_UNLICENSED_INFO', obj);
  292. // this.$nextTick(() => {
  293. // this.qrCodesRule(obj.code);
  294. // })
  295. },
  296. error: (res) => {
  297. console.log('H5页面扫码获取到的参数——失败res', res);
  298. // console.log(242, res);
  299. },
  300. });
  301. } catch (err) {
  302. console.log('H5页面扫码获取到的参数——失败err', err);
  303. }
  304. }
  305. },
  306. // 处理扫码结果: 组装参数,剩余流程,在 缴费支付页面 实现
  307. async qrCodesRule(code) {
  308. try {
  309. const qrCodesres = await qrCodes(code); // 无牌车扫码
  310. // 记录buildingId,确保 buildingId 是最新的数据
  311. window.localStorage.setItem('buildingId', qrCodesres.buildingId);
  312. // 如果是无牌车扫码:出场
  313. console.log('模拟出场', this.unlicensedInfo?.type);
  314. this.$store.commit('cachedViews/DEL_CACHED_VIEW', {
  315. name: 'parkingFeeDetail',
  316. });
  317. if (this.unlicensedInfo?.type === 'unlicensedOut') {
  318. setTimeout(() => {
  319. this.$router.replace({
  320. path: 'parkingFeeDetail',
  321. query: {
  322. gateId: qrCodesres.gateId,
  323. vehicleNo: '',
  324. type: 'unlicensedOut'
  325. }
  326. })
  327. }, 300)
  328. return
  329. }
  330. // 如果是无牌车扫码:入场
  331. const unlicensedCarCheckInres = await unlicensedCarCheckIn({ // 获取无牌车牌
  332. gateId: qrCodesres.gateId
  333. });
  334. this.type = 'success'
  335. this.vehicleNo = unlicensedCarCheckInres.vehicleNo
  336. // 前往 缴费支付页面
  337. // this.$router.replace({
  338. // path: 'parkingFeeMsg',
  339. // query: {
  340. // type: 'success',
  341. // vehicleNo: unlicensedCarCheckInres.vehicleNo
  342. // }
  343. // })
  344. } catch (err) {
  345. // 车场扫描道闸入口,发现无车/车场扫描道闸入口,发现有牌车 >>> 停止往下执行,默认提示报错信息
  346. if (/CAR_HAS_PLATE/.test(err.code)) {
  347. return
  348. }
  349. if (/CAR_NOT_FOUND|UNLICENSED_PLATE_ACQUISITION_FAILED/.test(err.code)) {
  350. setTimeout(() => {
  351. // 如果是其他错误的话,则继续往下执行
  352. this.$router.replace({
  353. path: 'parkingFeeMsg',
  354. query: {
  355. type: this.unlicensedInfo?.type === 'unlicensedOut' ? 'outFail' : 'fail'
  356. // this.type
  357. }
  358. })
  359. this.pageInit()
  360. }, 300)
  361. }
  362. }
  363. },
  364. goHome() {
  365. if ((this.isLogin === 'notLoggedIn' || this.isLogin === 'loginDenied') && this.$route.query?.loginCount === 'undefined') {
  366. wx.miniProgram.redirectTo({
  367. "url": "/pages/package-parkingFee/parkingFeeWebViewLogin?needLogin=1&fromPage=home" // 去 login 页面 1 去登录
  368. })
  369. return
  370. }
  371. this.$router.replace({
  372. path: 'home',
  373. });
  374. },
  375. },
  376. };
  377. </script>
  378. <style lang="less" scoped>
  379. .bg {
  380. background-color: #FBFCFF;
  381. }
  382. .pay-box {
  383. text-align: center;
  384. height: calc(100vh - 100px);
  385. //margin-top: 84px;
  386. padding-top: 100px;
  387. .pay-log {
  388. width: 523px;
  389. display: block;
  390. margin: 0 auto;
  391. }
  392. .info {
  393. font-family: 'PingFang SC';
  394. font-style: normal;
  395. font-weight: 400;
  396. font-size: 28px;
  397. line-height: 39px;
  398. text-align: center;
  399. color: #919baa;
  400. }
  401. }
  402. .success-box,
  403. .fail-box {
  404. //margin-top: 123px;
  405. //padding-top: 123px;
  406. text-align: center;
  407. padding: 123px 24px 0;
  408. .icon {
  409. width: 80px;
  410. height: 80px;
  411. display: block;
  412. margin: 0 auto 42px;
  413. }
  414. .status-title {
  415. font-family: 'PingFang SC';
  416. font-style: normal;
  417. font-weight: 400;
  418. font-size: 36px;
  419. line-height: 50px;
  420. color: #333333;
  421. margin-bottom: 42px;
  422. }
  423. .status-info {
  424. font-family: 'PingFang SC';
  425. font-style: normal;
  426. font-weight: 400;
  427. font-size: 26px;
  428. line-height: 40px;
  429. text-align: center;
  430. color: #999999;
  431. }
  432. .card-box {
  433. height: 230px;
  434. margin-top: 42px;
  435. margin-bottom: 52px;
  436. background: #fbfcff;
  437. border: 1px solid #d9dbe0;
  438. border-radius: 4px;
  439. display: flex;
  440. img {
  441. width: 288px;
  442. height: 142px;
  443. display: block;
  444. margin: auto 0;
  445. }
  446. .car-number {
  447. //margin-top: 68px;
  448. display: flex;
  449. flex-direction: column;
  450. justify-content: center;
  451. .number {
  452. font-size: 50px;
  453. line-height: 56px;
  454. font-weight: 600;
  455. color: #333333;
  456. margin-bottom: 23px;
  457. letter-spacing: 6.5px;
  458. }
  459. .tips {
  460. color: #999999;
  461. text-align: left;
  462. }
  463. }
  464. }
  465. .out-fail-log {
  466. width: 535px;
  467. height: 410px;
  468. display: block;
  469. margin: 0 auto 68px;
  470. }
  471. .unlicensed-scan {
  472. width: 60px;
  473. height: 60px;
  474. margin-right: 16px;
  475. }
  476. }
  477. </style>