Bläddra i källkod

Merge pull request #564 from tron/lock/release-3.14.0/KIP-17965

feat(KIP-17965):  无牌车月泊领取临时车牌二维码
Tron 1 år sedan
förälder
incheckning
6726e7dd81
3 ändrade filer med 511 tillägg och 1 borttagningar
  1. 3 1
      src/api/parking/index.js
  2. 504 0
      src/pages/parkingFeeV2/qrCodeLicensedCar.vue
  3. 4 0
      src/routes/indexV2.js

+ 3 - 1
src/api/parking/index.js

@@ -84,7 +84,9 @@ export function currentUnlicensedPlate() {
 export function unlicensedCarCheckIn(params) {
   return window.requestms.post(`/parking/unlicensed-car-check-in`, params, { loading: true });
 }
-
+export function unlicensedCarGetVehicle(qrcode) {
+  return window.requestms.get(`/parking/unlicensed-car-get-vehicle-no?qrCodeId=${qrcode}`, { loading: true });
+}
 /* 开票相关接口api */
 
 // 未开票列表

+ 504 - 0
src/pages/parkingFeeV2/qrCodeLicensedCar.vue

@@ -0,0 +1,504 @@
+<template>
+  <div :class="[theme, 'bg']">
+    <!-- 领取临牌:成功 -->
+    <!-- <div @click="qrCodesRule('8b0571a1')">test</div> -->
+    <div class="success-box" v-if="type === 'success'">
+      <img class="icon" :src="require(`@/pages/parkingFee/static/images/unlicensed/success.png`)" />
+      <div class="status-title">临牌领取成功</div>
+      <!-- <div class="status-info">若未正常抬杠,请关闭当前页面重新扫码</div> -->
+      <div class="status-info">或联系车场管理人员处理</div>
+      <div class="card-box">
+        <img :src="`${require(`@/pages/parkingFee/static/images/unlicensed-0.png`)}`" style="margin-left: 10px"/>
+        <div class="car-number">
+          <div class="number">{{ vehicleNo | formatCarno}}</div>
+          <div class="tips">车辆类型:无牌车辆</div>
+        </div>
+      </div>
+      <k-button title="进入首页" disabledColor="#D1D2D9" @click="goHome" />
+    </div>
+    <!-- 领取临牌:失败 -->
+    <div class="fail-box" v-if="type === 'fail'">
+      <img class="icon" :src="require(`@/pages/parkingFee/static/images/unlicensed/fail.png`)" />
+      <div class="status-title">临牌领取失败</div>
+      <div class="status-info">请关闭当前页面重新扫码</div>
+      <div class="status-info">或联系车场管理人员处理</div>
+      <div class="card-box">
+        <img :src="`${require(`@/pages/parkingFee/static/images/unlicensed-0.png`)}`" style="margin-left: 10px"/>
+        <div class="car-number">
+          <div class="number">临K ???</div>
+          <div class="tips">车辆类型:无牌车辆</div>
+        </div>
+      </div>
+      <k-button disabledColor="#D1D2D9" @click="scanCarCode">
+        <template v-slot:left>
+          <img class="unlicensed-scan" :src="`${require(`@/pages/parkingFee/static/images/unlicensed-scan.png`)}`" />
+        </template>
+        重新扫码
+      </k-button>
+    </div>
+    <!-- 支付成功 -->
+    <div class="pay-box" v-if="type === 'pay'">
+      <img class="pay-log" :src="require(`@/pages/parkingFee/static/images/unlicensed/pay.svg`)" />
+      <span class="info">您已缴费成功,欢迎下次光临</span>
+    </div>
+    <!-- 出场临牌:失败 -->
+    <div class="fail-box" v-if="type === 'outFail'">
+      <img class="out-fail-log" :src="require(`@/pages/parkingFee/static/images/unlicensed/no-car-out.svg`)" />
+      <k-button disabledColor="#D1D2D9" @click="scanCarCode">
+        <template v-slot:left>
+          <img class="unlicensed-scan" :src="`${require(`@/pages/parkingFee/static/images/unlicensed-scan.png`)}`" />
+        </template>
+        重新扫码
+      </k-button>
+<!--      <span class="info">您已缴费成功,欢迎下次光临</span>-->
+    </div>
+  </div>
+</template>
+
+<script>
+import { unlicensedCarGetVehicle } from "@/api/parking";
+import baseMixins from "@/pages/parkingFee/mixins/base";
+import uni from '@/utils/uniHooks';
+import { initWxJsSdkConfig } from '@/utils/login';
+import { getPlatform,getUrlParams, theCommunicationBetweenWechatAndH5IsNormal, extractValuesFromString } from '@/utils';
+import {mapState} from "vuex";
+
+export default {
+  mixins: [baseMixins],
+  name: 'parkingFeeMsg',
+  data() {
+    return {
+      type: '',
+      vehicleNo: '',
+      qrCode: ''
+    };
+  },
+  computed:{
+    ...mapState({
+      unlicensedInfo: (state) => state.unlicensedInfo,
+      isLogin: (state) => state.isLogin,
+    })
+  },
+  async created() {
+    // setTimeout(() => {
+    //   window?.toWXSendMsg({
+    //     type: 'nowRoute',
+    //     options: {
+    //       fullPath: '1'
+    //     },
+    //   });
+    // }, 100);
+    // setTimeout(() => {
+    //   uni.setNavigationBarTitle({
+    //     title: '停车缴费提示',
+    //   });
+    // }, 300);
+    // const platform = getPlatform();
+    // console.log(this.$route.query);
+    this.pageInit()
+    try {
+      // await initWxJsSdkConfig(['checkJsApi', 'scanQRCode']);
+    } catch (e) {
+      console.log(e)
+    }
+  },
+  mounted() {},
+  methods: {
+    async getVehicleNo (qrcode) {
+      try {
+        const Vehicle = await unlicensedCarGetVehicle(qrcode); // 无牌车扫码
+        this.type = 'success'
+        console.log('this.type::', this.type)
+        this.vehicleNo = Vehicle
+        console.log('无牌车车牌:::', Vehicle)
+      } catch(err) {
+        this.type = 'fail'
+        console.log('无牌车车牌领取失败', err)
+      }
+    },
+    pageInit(){
+      console.log('this.$route.query::::', this.$route.query)
+      if (this.$route.query?.qrCode) {
+        this.qrCode = this.$route.query?.qrCode;
+        this.getVehicleNo(this.qrCode)
+      }
+    },
+    getParam(url, key) {
+      let u = url.split('?')[1]
+      let l = u.split('&')
+      let o = {}
+      l.forEach(i => {
+        let key = i.split('=')[0]
+        let value = i.split('=')[1]
+        if (key == 'type') {
+          if (value != 'test') {
+            o[key] = value
+          }
+        } else {
+          o[key] = value
+        }
+      })
+      return o[key]
+    },
+    get_sa_utm(options) {
+      return new Promise((resolve) => {
+        try {
+          let temp = ''
+          // #ifdef MP-WEIXIN
+          if (options.q) {
+            temp = decodeURIComponent(options.q)
+          }
+          // #endif
+          // #ifdef MP-ALIPAY
+           // 如果是支付宝这边已经解析过了,则返回false,不做二次解析
+          // const query = CacheTool.getMiniAppOptionsQuery()
+          console.log('options.options::::', options.options)
+          if (options.options.qrCode) {
+            temp = decodeURIComponent(options.options.qrCode)
+          }
+          
+          // #endif
+          // temp = https://crm.kerryplus.com/t-parking?sa_utm=iE
+          if (temp && temp.indexOf('?') > -1) {
+            const sa_utm = this.getParam(temp, 'sa_utm');
+            console.log('utm参数', sa_utm)
+            return resolve(sa_utm || false)
+          }
+          console.log('二码合一参数解析失败', temp);
+          return resolve(false)
+        } catch (err) {
+          resolve(false)
+        }
+      })
+    },
+    asyncRequest(url, data, method = 'GET') {
+      return new Promise((resolve, reject) => {
+        uni.request({
+          url: url,
+          data: data,
+          header: JSON.parse(uni.getStorageSync('handleUser') || "{}"),
+          method: method,
+          success: (res) => {
+            resolve(res.data);
+          },
+          fail: (err) => {
+            reject(err);
+          }
+        });
+      });
+    },
+    // 在异步函数中调用异步请求
+    fetchData(id) {
+      return new Promise(async (resolve, reject) => {
+        try {
+          const res = await this.asyncRequest(`${window.QR_CODE_BASE_URL}/c/${id}`);
+          console.log('baseURLQrCode:::', res)
+          // console.log('baseURLQrCodebaseURLQrCode::', res.data);
+          if (res?.code == 200) {
+            // console.log('77777777', res, queryStringToObject(res.data.url));
+            // const [baseURL, key, value] = res.data.url.match(/(.*)=(.*)/)
+            if (res?.data?.url) {
+              // resolve({ key, value })
+              const insideUrl =
+              `/pages/package-parkingFee/parkingFeeWebViewLogin?${res.data.url}`
+            
+              // resolve(queryStringToObject(`t-page?${res.data.url}`))
+              // #ifdef MP-ALIPAY
+              my.navigateTo({
+                url: `/pages/package-parkingFee/parkingFeeWebViewLogin?${res.data.url}`,
+                complete: function (res) {
+                  console.log('回调::', res)
+                },
+                success: function (res) {
+                  console.log('跳转成功::', res)
+                },
+                fail: function (err) {
+                  console.log('跳转失败::', res)
+                }
+              });
+              // #endif
+            } else {
+              reject(false)
+            }
+          }
+        } catch (error) {
+          console.error('请求失败', error);
+        }
+      })
+    },
+    async getqrcode (options) {
+      
+      try {
+        const sa_utm =  await this.get_sa_utm(options);
+        if (sa_utm) {
+          const params = await this.fetchData(sa_utm)
+          
+          console.log('二码合一参数::::', params)
+        }
+        
+      } catch(err) {
+
+      }
+    },
+    // 无牌车闸机扫码
+    async scanCarCode() {
+      var _this = this
+      const runScanFn = (res) => {
+        /*
+         针对微信的小程序码进行的兼容改造
+         微信扫码结束之后的返回参数 {"errMsg": "scanCode:ok", "scanType": "WX_CODE", "charSet": "ISO8859-1", "rawData": "bGsoP3gyT1Aud3QpbW1JeHRfVHJsUjg4JnR5cGU9dW5saWNlbnNlZElu", "path": "pages/automatic/automaticIndex?scene=code%3D9988%26type%3DunlicensedIn"}
+         */
+        if(res.scanType && res.scanType === 'WX_CODE' && res?.path) {
+          const params = getUrlParams(`?${decodeURIComponent(res.path.replace(/.*scene=/g, ''))}`)
+          this.$store.commit('SET_UNLICENSED_INFO', params);
+          this.$nextTick(() => {
+            this.qrCodesRule(params.code);
+          })
+        }
+        // 兜底逻辑,如果是其他小程序扫描,则提取rawData,进行解析提取入参
+        if(res.scanType && res.scanType === 'WX_CODE' && !res?.path){
+          let path = atob(res.rawData)
+          path = path.replace(/.*([a-z0-9]{6}&type)/g, '8b$1')
+          const regex = /(\w+)&type=(\w+)/;
+          const match = path.match(regex);
+          const obj = { code: match[1], type: match[2] };
+          this.$store.commit('SET_UNLICENSED_INFO', obj);
+          this.$nextTick(() => {
+            this.qrCodesRule(obj.code);
+          })
+        }
+      };
+      // 微信小程序
+      // const platform = getPlatform();
+      if (this.isAlipayClient) {
+        // 判断微信小程序与 h5 是否正常通信
+        // const isReload = await theCommunicationBetweenWechatAndH5IsNormal()
+        // if(!isReload) {
+        //   uni.setStorageSync('isReload', 1)
+        //   window.location.reload()
+        //   return
+        // }
+         // 调用支付宝扫一扫功能
+            window.toWXSendMsg({
+              type: 'scanQRCode',
+            });
+            console.log('二码合一参数::::')
+            
+            // 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"}});
+            // TODO 兼容支付宝无牌车扫码
+            // 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"}})
+          window.subscribe('scanQRCodeOver', (options) => {
+            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"}});
+              // const sa_utm =  await this.get_sa_utm(options);
+              // console.log('sa_utm:::', sa_utm)
+              _this.getqrcode(options)
+             
+            
+            // runScanFn(options);
+          });
+      } else {
+        try {
+          this.$wx.scanQRCode({
+            needResult: 0, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
+            // onlyFromCamera: false,
+            // desc: 'scanQRCode desc',
+            // needResult: 0, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
+            // scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有
+            success: (res) => {
+              console.log('H5页面扫码获取到的参数——成功res2', res);
+              // let path = res.resultStr.replace(/.*([a-z0-9]{6}&type)/g, '8b$1')
+              // const regex = /(\w+)&type=(\w+)/;
+              // const match = path.match(regex);
+              // const obj = {code: match[1], type: match[2]};
+              // this.$store.commit('SET_UNLICENSED_INFO', obj);
+              // this.$nextTick(() => {
+              //   this.qrCodesRule(obj.code);
+              // })
+            },
+            error: (res) => {
+              console.log('H5页面扫码获取到的参数——失败res', res);
+              // console.log(242, res);
+            },
+          });  
+        } catch (err) {
+          console.log('H5页面扫码获取到的参数——失败err', err);
+        }
+        
+      }
+    },
+    // 处理扫码结果: 组装参数,剩余流程,在 缴费支付页面 实现
+    async qrCodesRule(code) {
+      try {
+        const qrCodesres = await qrCodes(code); // 无牌车扫码
+        // 记录buildingId,确保 buildingId 是最新的数据
+        window.localStorage.setItem('buildingId', qrCodesres.buildingId);
+        // 如果是无牌车扫码:出场
+        console.log('模拟出场', this.unlicensedInfo?.type);
+        this.$store.commit('cachedViews/DEL_CACHED_VIEW', {
+          name: 'parkingFeeDetail',
+        });
+        if (this.unlicensedInfo?.type === 'unlicensedOut') {
+          setTimeout(() => {
+            this.$router.replace({
+              path: 'parkingFeeDetail',
+              query: {
+                gateId: qrCodesres.gateId,
+                vehicleNo: '',
+                type: 'unlicensedOut'
+              }
+            })  
+          }, 300)
+          return
+        }
+        // 如果是无牌车扫码:入场
+        const unlicensedCarCheckInres = await unlicensedCarCheckIn({ // 获取无牌车牌
+          gateId: qrCodesres.gateId
+        });
+        this.type = 'success'
+        this.vehicleNo = unlicensedCarCheckInres.vehicleNo
+        // 前往 缴费支付页面
+        // this.$router.replace({
+        //   path: 'parkingFeeMsg',
+        //   query: {
+        //     type: 'success',
+        //     vehicleNo: unlicensedCarCheckInres.vehicleNo
+        //   }
+        // })
+      } catch (err) {
+        // 车场扫描道闸入口,发现无车/车场扫描道闸入口,发现有牌车 >>> 停止往下执行,默认提示报错信息
+        if (/CAR_HAS_PLATE/.test(err.code)) {
+          return
+        }
+        if (/CAR_NOT_FOUND|UNLICENSED_PLATE_ACQUISITION_FAILED/.test(err.code)) {
+          setTimeout(() => {
+            // 如果是其他错误的话,则继续往下执行
+            this.$router.replace({
+              path: 'parkingFeeMsg',
+              query: {
+                type: this.unlicensedInfo?.type === 'unlicensedOut' ? 'outFail' : 'fail'
+                // this.type
+              }
+            }) 
+            this.pageInit()
+          }, 300)
+        }
+      }
+    },
+    goHome() {
+      if ((this.isLogin === 'notLoggedIn' || this.isLogin === 'loginDenied') && this.$route.query?.loginCount === 'undefined') {
+        wx.miniProgram.redirectTo({
+          "url": "/pages/package-parkingFee/parkingFeeWebViewLogin?needLogin=1&fromPage=home" // 去 login 页面 1 去登录
+        })
+        return
+      }
+      this.$router.replace({
+        path: 'home',
+      });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.bg {
+  background-color: #FBFCFF;
+}
+.pay-box {
+  text-align: center;
+  height: calc(100vh - 100px);
+  //margin-top: 84px;
+  padding-top: 100px;
+  .pay-log {
+    width: 523px;
+    display: block;
+    margin: 0 auto;
+  }
+  .info {
+    font-family: 'PingFang SC';
+    font-style: normal;
+    font-weight: 400;
+    font-size: 28px;
+    line-height: 39px;
+    text-align: center;
+    color: #919baa;
+  }
+}
+
+.success-box,
+.fail-box {
+  //margin-top: 123px;
+  //padding-top: 123px;
+  text-align: center;
+  padding: 123px 24px 0;
+  .icon {
+    width: 80px;
+    height: 80px;
+    display: block;
+    margin: 0 auto 42px;
+  }
+  .status-title {
+    font-family: 'PingFang SC';
+    font-style: normal;
+    font-weight: 400;
+    font-size: 36px;
+    line-height: 50px;
+    color: #333333;
+    margin-bottom: 42px;
+  }
+  .status-info {
+    font-family: 'PingFang SC';
+    font-style: normal;
+    font-weight: 400;
+    font-size: 26px;
+    line-height: 40px;
+    text-align: center;
+    color: #999999;
+  }
+  .card-box {
+    height: 230px;
+    margin-top: 42px;
+    margin-bottom: 52px;
+    background: #fbfcff;
+    border: 1px solid #d9dbe0;
+    border-radius: 4px;
+    display: flex;
+
+    img {
+      width: 288px;
+      height: 142px;
+      display: block;
+      margin: auto 0;
+    }
+    .car-number {
+      //margin-top: 68px;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      .number {
+        font-size: 50px;
+        line-height: 56px;
+        font-weight: 600;
+        color: #333333;
+        margin-bottom: 23px;
+        letter-spacing: 6.5px;
+      }
+      .tips {
+        color: #999999;
+        text-align: left;
+      }
+    }
+  }
+  .out-fail-log {
+    width: 535px;
+    height: 410px;
+    display: block;
+    margin:  0 auto 68px;
+  }
+  
+  .unlicensed-scan {
+    width: 60px;
+    height: 60px;
+    margin-right: 16px;
+  }
+}
+</style>

+ 4 - 0
src/routes/indexV2.js

@@ -82,6 +82,10 @@ export const routes = [
         path: '/parkingFeeMsg',
         name: 'parkingFeeMsg',
         component: () => import('@/pages/parkingFeeV2/parkingFeeMsg.vue'),
+    },{
+        path: '/qrCodeLicensedCar',
+        name: 'qrCodeLicensedCar',
+        component: () => import('@/pages/parkingFeeV2/qrCodeLicensedCar.vue'),
     },{
         path: '/parkingFeeSuccess',
         name: 'parkingFeeSuccess',