天天看點

二維碼收款——前端UI

二維碼收款前端,使用Vue

線下店鋪一般使用的店鋪收款碼,有銀行提供的,有通聯的,結合語音播報,友善快捷,下面我們來做一個類似的屬于自己收款系統

本系列文章分3部分

  1. 前端UI
  2. 後端系統
  3. 通知系統

UI界面直接借鑒微信(哈哈),如下圖

二維碼收款——前端UI

 前端使用Vue,3個檔案,pay.html,pay.js,pay.css

簡要說明

  1. 數字鍵盤使用table标簽布局,可以完美的模拟微信鍵盤的效果,按鍵按下時有高亮效果,通過js控制隻能輸入有效的數值
  2. 請求時URL中帶有商戶号參數,例如xxx.com/pay.html?mercNo=xxx,mercNo就是商戶号,商戶資訊包含商戶名,商戶LOGO,請求商戶資訊後,顯示出來
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, user-scalable=no"
    />
    <title>付款</title>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/[email protected]/lib/index.css"
    />
    <link rel="stylesheet" href="pay.css" />
    <script src="../js/common.js"></script>
    <style type="text/css">
      [v-cloak] {
        display: none;
      }
    </style>
  </head>
  <body>
    <div class="w-warp" id="main" v-cloak>
      <div class="info">
        <div class="text">
          <h4>付款給</h4>
          <p style="color: gray">{{mercInfo.mercName}}</p>
        </div>
        <div class="head"><img :src="mercInfo.mercLogo" width="60px" height="60px" /></div>
      </div>
      <div class="amount">
        <h5>金額</h5>
        <div class="input">
          <p class="symbol">¥</p>
          <p class="placeholder" v-if="!activeInput">輸入金額</p>
          <p class="num" v-else>{{validNum}}</p>
        </div>
      </div>
      <div class="addmemo">
        <p @click="showMemoDialog=true">{{memo.length>0 ? memo : '添加備注'}}</p>
      </div>
      <div class="keyboard">
        <table cellspacing="8" >
          <tr>
            <td @click="onKeyboard(1)" @touchstart="keyHighlight['1']=true" @touchend="keyHighlight['1']=false" :class="{'keyhighlight': keyHighlight['1']}">1</td>
            <td @click="onKeyboard(2)" @touchstart="keyHighlight['2']=true" @touchend="keyHighlight['2']=false" :class="{'keyhighlight': keyHighlight['2']}">2</td>
            <td @click="onKeyboard(3)" @touchstart="keyHighlight['3']=true" @touchend="keyHighlight['3']=false" :class="{'keyhighlight': keyHighlight['3']}">3</td>
            <td style="width: 22%;" @click="onKeyboard('DEL')" @touchstart="keyHighlight['DEL']=true" @touchend="keyHighlight['DEL']=false" :class="{'keyhighlight': keyHighlight['DEL']}">DEL</td>
          </tr>
          <tr>
            <td @click="onKeyboard(4)" @touchstart="keyHighlight['4']=true" @touchend="keyHighlight['4']=false" :class="{'keyhighlight': keyHighlight['4']}">4</td>
            <td @click="onKeyboard(5)" @touchstart="keyHighlight['5']=true" @touchend="keyHighlight['5']=false" :class="{'keyhighlight': keyHighlight['5']}">5</td>
            <td @click="onKeyboard(6)" @touchstart="keyHighlight['6']=true" @touchend="keyHighlight['6']=false" :class="{'keyhighlight': keyHighlight['6']}">6</td>
            <td rowspan="3" class="paybtn" @click="onKeyboard('pay')" @touchstart="keyHighlight['pay']=true" @touchend="keyHighlight['pay']=false" :class="{'paybtnhighlight': keyHighlight['pay']}">付款</td>
          </tr>
          <tr>
            <td @click="onKeyboard(7)" @touchstart="keyHighlight['7']=true" @touchend="keyHighlight['7']=false" :class="{'keyhighlight': keyHighlight['7']}">7</td>
            <td @click="onKeyboard(8)" @touchstart="keyHighlight['8']=true" @touchend="keyHighlight['8']=false" :class="{'keyhighlight': keyHighlight['8']}">8</td>
            <td @click="onKeyboard(9)" @touchstart="keyHighlight['9']=true" @touchend="keyHighlight['9']=false" :class="{'keyhighlight': keyHighlight['9']}">9</td>
          </tr>
          <tr>
            <td colspan="2" @click="onKeyboard(0)" @touchstart="keyHighlight['0']=true" @touchend="keyHighlight['0']=false" :class="{'keyhighlight': keyHighlight['0']}">0</td>
            <td @click="onKeyboard('.')" @touchstart="keyHighlight['.']=true" @touchend="keyHighlight['.']=false" :class="{'keyhighlight': keyHighlight['.']}">.</td>
          </tr>
        </table>
      </div>

      <!-- 備注輸入框 -->
      <van-dialog v-model="showMemoDialog" confirm-button-color='#1aad19'>
        <van-field v-model="memo" label="備注" placeholder="輸入備注" size='large'/>
      </van-dialog>

      </div>
    </div>
  </body>

  <!-- 引入 Vue 和 Vant 的 JS 檔案 -->
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/vant.min.js"></script>
  <script src="../js/config.js"></script>
  <script src="pay.js"></script>

</html>      
new Vue({
  el: '#main',
  data: {
    activeInput: false, //激活輸入
    inputText: [], //輸入的字元隊列
    validNum: 0, //有效的輸入數字
    keyHighlight: {
      1: false,
      2: false,
      3: false,
      4: false,
      5: false,
      6: false,
      7: false,
      8: false,
      9: false,
      0: false,
      '.': false,
      DEL: false,
      pay: false,
    }, //鍵盤高亮
    showMemoDialog: false, //顯示備注輸入框
    memo: '',
    mercInfo: {}, //商戶資訊
  },
  created() {},
  mounted() {
    //商戶号
    let mercNo = getQueryVariable('mercNo');
    refu.get(`/a/merc/info?mercNo=${mercNo}`).then((res) => {
      this.mercInfo = res.result;
    });
  },
  methods: {
    //驗證金額是否有效
    validNumber(t) {
      var reg = /((^[1-9]\d*)|^0)(\.\d{0,2}){0,1}$/;
      return reg.test(t);
    },

    //點選鍵盤
    onKeyboard(key) {
      if (key == 'DEL') {
        this.inputText.pop();
        if (this.inputText.length > 0) {
          var s = this.inputText.join('');
          this.validNum = s;
        } else {
          this.validNum = 0;
          this.activeInput = false;
        }
        return;
      }
      if (key == 'pay') {
        console.log('輸入金額:', this.validNum);
        if (this.validNum == '0') return;
        let amount = parseFloat(this.validNum);
        if (!this.testWeixinOk()) {
          refu.toast('請在微信内打開');
          return;
        }
        //發起支付請求
        this.requestPay(this.mercInfo.mercNo, amount, this.memo).then((res) => {
          this.invokeWxpayJsApi(res.result);
        });
        return;
      }
      this.inputText.push(key);
      var s = this.inputText.join('');
      if (this.validNumber(s)) {
        this.validNum = s;
        this.activeInput = true;
      } else {
        this.inputText.pop();
      }
    },

    //發起支付請求
    async requestPay(mercNo, amount, memo) {
      let param = {
        mercNo: mercNo,
        amount: amount,
        memo: memo,
        channel: 'wxpay',
      };
      return await refu.post('/a/create/pay/order', param);
    },

    //檢測微信環境
    testWeixinOk() {
      return typeof WeixinJSBridge != 'undefined';
    },

    //拉起微信支付
    invokeWxpayJsApi(param) {
      WeixinJSBridge.invoke('getBrandWCPayRequest', param, function (res) {
        if (res.err_msg === 'get_brand_wcpay_request:ok') {
          // 使用以上方式判斷前端傳回,微信團隊鄭重提示:
          // res.err_msg将在使用者支付成功後傳回ok,但并不保證它絕對可靠。
          window.location.href = SiteInfo.paySuccessRedirect;
        }
      });
    },
  },
  filters: {},
  computed: {},
});      
body {
  background: #ededed;
  margin: 0;
}

p,
h4 {
  margin: 0.5rem 0rem;
}

.w-warp {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.info {
  display: flex;
  padding: 1rem 1rem;
  align-items: center;
}

.info .text {
  flex-grow: 1;
}

.info .head {
  flex-grow: 0;
}

.amount {
  flex-grow: 1;
  padding: 0.5rem 1rem;
  background-color: white;
  border-top-left-radius: 0.8rem;
  border-top-right-radius: 0.8rem;
}

.amount .input {
  display: flex;
  align-items: flex-end;
  font-size: 30px;
  font-weight: bold;
  border-bottom: solid 1px lightgray;
}

.input .symbol {
  margin: 0.5rem 0.6rem 0.5rem 0rem;
}

.input .placeholder {
  font-weight: normal;
  color: lightgray;
}

.addmemo {
  display: flex;
  justify-content: center;
  color: #726f89;
  background: white;
  padding: 1rem 0;
  font-size: 16px;
}

.keyboard table {
  width: 100%;
}

.keyboard td {
  font-size: 18;
  font-weight: bold;
  background: white;
  border-radius: 10px;
  border: none;
  text-align: center;
  padding: 0.8rem 0rem;
}

.keyboard .keyhighlight {
  background: #dedede;
}

.keyboard .paybtn {
  background: #1aad19;
  color: white;
}

.keyboard .paybtnhighlight {
  background: #07ae56;
}      

下一篇,後端API

繼續閱讀