二维码收款——前端UI

线下店铺一般使用的店铺收款码,有银行提供的,有通联的,结合语音播报,方便快捷,下面我们来做一个类似的属于自己收款系统

本系列文章分3部分

  1. 前端UI
  2. 后端系统
  3. 通知系统

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/vant@2.12/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=" 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/vue@2.6/dist/vue.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vant@2.12/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

 

原文地址:https://www.cnblogs.com/ieinstein/p/15756010.html