二維碼收款前端,使用Vue
線下店鋪一般使用的店鋪收款碼,有銀行提供的,有通聯的,結合語音播報,友善快捷,下面我們來做一個類似的屬于自己收款系統
本系列文章分3部分
- 前端UI
- 後端系統
- 通知系統
UI界面直接借鑒微信(哈哈),如下圖

前端使用Vue,3個檔案,pay.html,pay.js,pay.css
簡要說明
- 數字鍵盤使用table标簽布局,可以完美的模拟微信鍵盤的效果,按鍵按下時有高亮效果,通過js控制隻能輸入有效的數值
- 請求時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