零售收银终端CheckoutPad_ox_as
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

696 lines
20 KiB

<template>
<view class="pay-page">
<keyboard-listener style="width: 1rpx;" @keyup="onKeyup" @keydown="onKeydown" ></keyboard-listener>
<view v-if="paySuccessStatus==0">
<view-header class="mheader" :backgroundCustom="('none')" :needBorder="(false)"
@closeUse=""></view-header>
<paying v-if="true" :payTotal="payTotal" @clickVipCard="handle_clickVipCard"></paying>
</view>
<pay-success :paySuccessInfo="dataQuery" @clickOpenDoor ="handle_clickOpenDoor" v-else></pay-success>
<!-- 手机尾号后4位数验证 -->
<input-box-end-phone v-if="showEndPhoneBox" :maskHeight="screenHeight" :maxInput="4"
@showIt="handle_closeEndPhoneBox" @clickCommit="handle_clickCommit_end_phone"></input-box-end-phone>
<!-- 条形码输入框 -->
<!-- <input-qr-box v-if="false" :maskHeight="screenHeight" :maxInput="11"
@showIt="handle_closeQRInputBox" @clickCommit="handle_clickCommit_qr"></input-qr-box> -->
</view>
</template>
<script>
// #ifdef APP-PLUS
const ox_pad_printer = uni.requireNativePlugin('OX_Pad_Printer');
// #endif
import print from "@/utils/print";
import util from "@/utils/util.js"
import {
API
} from "@/utils/api";
import {
ox
} from "@/utils/server";
import dictKeyboard from '@/utils/dictOfKeyboard'
import viewHeader from '@/components/index/view_header.nvue';//头部
import inputQrBox from '@/components/index/keyboard/input_box_QR.vue'; //条形码
import inputBoxEndPhone from '@/components/index/keyboard/input_box_end_phone.vue';//手机号尾号
import paying from '@/components/pay/paying.vue';//支付中
import paySuccess from '@/components/pay/success.vue';//支付成功
import keyboardListener from '@/components/index/keyboard/keyboard_listener.vue';//键盘监听
export default {
components: {
'view-header': viewHeader,
'input-box-end-phone': inputBoxEndPhone,
'paying': paying,
'pay-success': paySuccess,
'input-qr-box': inputQrBox,
'keyboard-listener': keyboardListener,
},
computed: {
payTotal() {//子组件中支付金额
let _payTotal = this.dataQuery.amount||0
return _payTotal||"0.00"
}
},
watch:{
},
data() {
return {
showPageStandby: false,
showEndPhoneBox:false,
screenHeight: '1000',
footerHeight: '',
paySuccessStatus:0,
decryptList: [],
strResult: "",
dataQuery:"",
reCheckOrderCount:0,//重新查询支付结果次数
reCheckOrderCountMax:30,//重新查询支付结果最大次数
}
},
async onLoad(res) {
console.log("pay onload:",res)
if(res){this.setPayData(res)}
// let resres = this.getTestPrintData()
// this.printOrder(resres)
},
async onReady() {
await this.setPageHeight()
// this.testAcceptScanCode()
this.preMakeOrder()
},
onUnload(){
this.clearMakeOrder()
},
methods: {
//测试扫码,弹窗提醒,点确定时执行acceptScanCode
testAcceptScanCode(){
uni.showModal({
title: '模拟扫码',
content: '模拟扫码,点确定时执行acceptScanCode',
showCancel: true,
confirmText: '确定',
success: (res) => {
if (res.confirm) {
console.log('用户点击确定');
this.acceptScanCode("282659672061313993")
}
}
});
},
setPayData(res){
console.log("setPayData:",res)
res = JSON.parse(res.query)
console.log("setPayData1:",res)
let _data = {
goods_data:res.shopList,
amount:res.shopListTotalPrice,
}
console.log("setPayData2:",_data)
this.dataQuery = _data
},
// 字符数字10,11,12,13,14,15开头是WECHAT_MICROPAY,28是ALI_BARCODEPAY
getPayType(code){
let pay_type = ""
if(code.indexOf("10") == 0 || code.indexOf("11") == 0 || code.indexOf("12") == 0 || code.indexOf("13") == 0 || code.indexOf("14") == 0 || code.indexOf("15") == 0){
pay_type = "WECHAT_MICROPAY"
}else if(code.indexOf("28") == 0){
pay_type = "ALI_BARCODEPAY"
}else{
pay_type = null
}
return pay_type
},
async preMakeOrder(){
this.makeInfo = await this.makeOrder()
if(!this.makeInfo?.order_no)return util.showNone("生成订单失败,请重新下单!")
},
clearMakeOrder(){
this.makeInfo = null
},
async acceptScanCode(code) {
console.log("acceptScanCode:",code)
// util.showNone("支付中...")
util.showLoad("交易处理中...")
let pay_code = code
let pay_type = this.getPayType(pay_code)
if(pay_type == null){
util.showNone("支付类型未知,请重试!")
return
}
// await this.makeOrder((makeInfo)=>{
// console.log("makeInfo-result",makeInfo)
// }).then((orderInfo)=>{
// console.log("orderInfo-result",orderInfo)
// return this.payOrder({
// "order_no":orderInfo.order_no,
// pay_code,pay_type
// })
// })
let order_no = this.makeInfo?.order_no||""
if(!order_no)return util.showNone("支付失败,请重新下单!")
await this.payOrder({
"order_no":order_no,
pay_code,pay_type
})
.then((payInfo)=>{
console.log("payInfo-result:",payInfo)
return this.checkOrder(payInfo)
}).then(async (checkInfo)=>{
console.log("最终checkInfo:",checkInfo)
let {payRes,checkRes} = checkInfo
// util.showNone(payRes.res.respMsg+"-"+payRes.res.respCode)
if(checkRes.pay_order.status == "1"){
util.showNone(checkRes.res.respMsg)
this.paySuccessStatus = 1
this.printOrder(checkInfo)
}else{
// util.showNone(checkRes.res.respMsg+"-"+checkRes.res.respCode)
let msg = checkRes.res.respMsg||"未知原因"+"-"+checkRes.res.respCode+'\n';
let {orderStatus,requestDate,respCode,respMsg,tradeNo,transNo} = checkRes.res
if(orderStatus=="PROCESSING"){
//支付失败
console.log("等待支付中,需要继续查询支付结果")
uni.showModal({
title: '等待支付中...',
content: "如您已确认付款成功,请点击确认,继续查询支付结果!",
showCancel: false,
confirmText: '确认已支付成功',
success: async (res) => {
if (res.confirm) {
console.log('用户点击确定');
if(util.getPageRoute()=='pages/pay/index'){
this.doLoopCheck(checkInfo)
}else{
util.showNone('请重新扫码使用')
}
}
}
});
}else{
await this.checkInfoReactive(checkInfo)
}
}
}).finally(()=>{
util.hideLoad()
})
},
async doLoopCheck(checkInfo){
let max = this.reCheckOrderCountMax
for(let i=0;i<=max;i++){
// let res = null
uni.showLoading({
title: '支付结果查询中...'+i+"/"+this.reCheckOrderCountMax,
mask: true
});
let recheckInfo = await this.checkOrder(checkInfo.payRes).then((_recheckInfo)=>{
return _recheckInfo
}).catch(err=>{
console.warn("reCheckOrder err:",err)
rj(false)
})
console.log("recheckInfo--1:",recheckInfo)
let {orderStatus,requestDate,respCode,respMsg,tradeNo,transNo} = recheckInfo.checkRes.res
console.log("doLoopCheck recheckInfo:",recheckInfo)
if(orderStatus=="SUCCESS"){
let {payRes,checkRes} = recheckInfo
// util.showNone(payRes.res.respMsg+"-"+payRes.res.respCode)
if(checkRes.pay_order.status == "1"){
util.showNone(checkRes.res.respMsg)
this.paySuccessStatus = 1
this.printOrder(checkInfo)
}
break
}else{
await util.asyncSetTimeOut(2000)
continue
}
}
uni.hideLoading()
},
async checkInfoReactive(checkInfo){ //废弃
return new Promise((rs,rj)=>{
console.log("checkInfoReactive",checkInfo)
let {payRes,checkRes} = checkInfo
let {orderStatus,requestDate,respCode,respMsg,tradeNo,transNo} = checkRes.res
// 200021,200008,,200004 需要重新下单
let msg = checkRes.res.respMsg||"未知原因"+"-"+checkRes.res.respCode+'\n';
if(checkRes.pay_order.status == "1"){
util.showNone(checkRes.res.respMsg)
this.paySuccessStatus = 1
this.printOrder(checkInfo)
rs(true)
}
else if(respCode=="200021"||respCode=="200008"||respCode=="200004"){
//重新下单
console.warn("需要重新下单")
uni.showModal({
title: '支付失败',
content: msg,
showCancel: false,
confirmText: '确定',
success: (res) => {
if (res.confirm) {
console.log('用户点击确定');
util.routeTo(`/pages/index/standby`, 'rL');
rs(true)
}
}
});
}else if(respCode=="200103"||orderStatus=="FAIL"){
//支付成功
msg = "支付失败code:"+checkRes.res.respCode+".请联系管理员!"
uni.showModal({
title: '支付失败',
content: msg,
showCancel: false,
confirmText: '确定',
success: (res) => {
if (res.confirm) {
console.log('用户点击确定');
util.routeTo(`/pages/index/standby`, 'rL');
rs(true)
}
}
});
}else if(orderStatus=="PROCESSING"&&this.reCheckOrderCount==0){
//支付失败
console.log("等待支付中,需要继续查询支付结果")
uni.showModal({
title: '等待支付中...',
content: "如您已确认付款成功,请点击确认,继续查询支付结果!",
showCancel: false,
confirmText: '确认已支付成功',
success: async (res) => {
if (res.confirm) {
console.log('用户点击确定');
rs(true)
// await this.reCheckOrder(checkInfo).then((recheckInfo)=>{
// console.warn("finally checkInfo:",recheckInfo)
// })
}
}
});
}else if(orderStatus=="PROCESSING"&&this.reCheckOrderCount>0){
rj(checkInfo)
}
})
},
loopReCheckOrder(_checkInfo){ //废弃
reCheckOrder(_checkInfo).then((checkInfo)=>{
loopReCheckOrder(checkInfo)
}).catch(err=>{
console.warn("loopReCheckOrder err:",err)
util.showNone("支付结果查询超时,请联系管理员!"+err)
})
},
async reCheckOrder(_checkInfo){ //废弃
let payRes = _checkInfo.payRes
return new Promise(async (rs,rj)=>{
this.reCheckOrderCount++
// uni.showLoading({
// title: '支付结果查询中...'+this.reCheckOrderCount+"/"+this.reCheckOrderCountMax,
// mask: true
// });
if(this.reCheckOrderCount>=this.reCheckOrderCountMax){
uni.hideLoading()
util.showNone("支付结果查询超时,请联系管理员!")
rj(false)
}else{
console.log("reCheckOrder:",payRes)
await this.checkOrder(payRes).then((recheckInfo)=>{
console.log("checkInfo:",recheckInfo)
return recheckInfo
}).then((_recheckInfo)=>{
}).catch(err=>{
console.warn("reCheckOrder err:",err)
rj(false)
})
let _recheckInfo = await this.checkOrder(payRes)
await this.checkInfoReactive(_recheckInfo).then((res)=>{
rs(res)
}).catch(err=>{
console.warn("reCheckOrder err:",err)
rj(false)
})
}
})
},
printOrder(checkInfo){
console.warn("printOrder checkInfo: ",checkInfo)
util.showNone("小票打印中...")
let {store_id,brand_id,token} = getApp().globalData.accountInfo
let stadiumInfo = uni.getStorageSync('stadiumInfo')
let {venue_name} = stadiumInfo
let {goods_data,amount} = this.dataQuery
let {finishedDate:time,transNo:order_no} = checkInfo.payRes.res
time = this.getTimeFromFinishedDate(time)
let print_info = {
...this.dataQuery,venue_name,time,order_no
}
// console.log("print_info:",print_info)
//调用打印插件打印TODO
// print.resetOXPrintInfo(print.testPrintInfo)
let obj = print.resetOXPrintInfo(print_info)
console.log("uni call android OX_Pad_Printer ------begin---!",obj)
// #ifdef APP-PLUS
return ox_pad_printer.execAPI({
api_name:"doPrint",
value:obj
},ret => {
console.log("ox_pad_printer doPrint result: " + JSON.stringify(ret))
})
// #endif
console.warn("请在APP离线打包环境测试打印功能")
},
getTimeFromFinishedDate(finishedDate) {
console.log(finishedDate)
// 提取年、月、日、时、分、秒
let year = finishedDate.substring(0, 4);
let month = finishedDate.substring(4, 6);
let day = finishedDate.substring(6, 8);
let hour = finishedDate.substring(8, 10);
let minute = finishedDate.substring(10, 12);
let second = finishedDate.substring(12, 14);
// 拼接成期望的格式
let formattedDate = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
// 返回结果
return formattedDate;
},
getTestPrintData(){
return {
"payRes": {
"order_no": "LS20240221105640796608",
"pay_code": "282659672061313993",
"pay_type": "ALI_BARCODEPAY",
"orderAmt": "1",
"transNo": "LS20240221105640796608",
"res": {
"channelOrderNo": "350105100000000000000382731226",
"finishedDate": "20240221105643",
"orderAmt": "1",
"orderStatus": "SUCCESS",
"payOrderNo": "322024022122001464831442986322",
"payType": "ALI_BARCODEPAY",
"respCode": "000000",
"respMsg": "交易成功",
"tradeNo": "19010520240221105641100306264795",
"transNo": "LS20240221105640796608"
}
},
"checkRes": {
"err": null,
"pay_order": {
"id": 1143536,
"appid": "",
"mchId": "ouxuanmchid-2d4af93d5fb4",
"user": "",
"out_trade_no": "LS20240221105640796608",
"transaction_id": "",
"group": "63",
"pay_way": "HuiShouQian",
"call_way": "WeChatMiniApp",
"total_fee": 0.01,
"refund_total_fee": 0,
"Title": "",
"subject": "零售订单",
"status": 1,
"refund_status": 0,
"flag": 0,
"tags": null,
"extension": {
"brand_id": 63,
"brand_stadium_logo": "https://imgcdn.ouxuanzhineng.cn/upload/63/logo/6b36b6b89557a7b7d90904f1a943a311.png",
"brand_stadium_name": "Test门店",
"stadium_id": 167,
"total_fee": 0.01,
"transaction_id": ""
},
"error_log": "",
"is_profit_sharing": 0,
"usefulness": "erp_goods_retail",
"medium": "money",
"invoice_status": "None",
"is_enable_expiration": 1,
"expiration_time": "2024-02-21T11:26:40+08:00",
"payment_time": "",
"remaining_refund": null
},
"res": {
"channelOrderNo": "350105100000000000000382731226",
"finishedDate": "20240221105644",
"goodsInfo": "LS20240221105640796608",
"memo": "{\"timeExpire\":\"20240221105746\",\"latitude\":\"0\",\"spbillCreateIp\":\"127.0.0.1\",\"longitude\":\"0\"}",
"orderAmt": "1",
"orderStatus": "SUCCESS",
"payOrderNo": "322024022122001464831442986322",
"payType": "ALI_BARCODEPAY",
"requestDate": "20240221105641",
"respCode": "000000",
"respMsg": "交易成功",
"tradeNo": "19010520240221105641100306264795",
"transNo": "LS20240221105640796608"
}
}
}
},
testPrinter(){
console.log("OX_Pad_Printer test------begin---!")
let obj = print.resetOXPrintInfo(print.testPrintInfo)
ox_pad_printer.execAPI({
api_name:"doPrint",
value:obj
},ret => {
console.log("testPrinter: " + JSON.stringify(ret))
})
},
async makeOrder(){
let {brand_id,token} = getApp().globalData.accountInfo
let store_id = uni.getStorageSync("stadiumInfo").id||""
let {goods_data,amount} = this.dataQuery
brand_id = parseInt(brand_id)
amount = parseFloat(amount)
let urlParams = {
store_id,brand_id,token,amount,
"goods_data": goods_data,
"pay_type": 7,
"other_pay_type":"付款码支付",
"card_no": "",
"mark": ""
}
console.log("makeOrder urlParams:", urlParams)
return ox.post({
url: API.makeOrder,
data: urlParams,
isDefaultGet: true,
failMsg: '操作失败!'
})
.then(res => {
util.hideLoad();
console.log("makeOrder suc: ", res)
return res
})
},
payOrder(orderInfo){
console.log("payOrder get param:",orderInfo)
let {order_no,pay_code,pay_type} = orderInfo
let {goods_data,amount} = this.dataQuery
let {store_id,brand_id,token} = getApp().globalData.accountInfo
let urlParams = {
order_no,
pay_code,
pay_type,
brand_id,
token,
"order_amt": amount, //未知TODO 问家华- 猜测是金额
"goods_info": order_no,
}
console.log("payOrder urlParams:", urlParams)
return ox.post({
url: API.payOrder,
data: urlParams,
isDefaultGet: true,
failMsg: "payOrder fail"
})
.then(res => {
console.log("payOrder suc: ", res)
let _pay_res = {
"order_no": order_no,
"pay_code": pay_code,
"pay_type": pay_type,
"orderAmt":res.res.orderAmt,
"transNo":res.res.transNo,
"res":res.res
}
return _pay_res
})
},
checkOrder(res){
let {store_id,brand_id,token} = getApp().globalData.accountInfo
let {transNo,orderAmt,payCode} = res
let urlParams= {
transNo,payCode,brand_id,token,orderAmt,
"manual": "yes",//是否手动
}
console.log("checkOrder urlParams:", urlParams)
return ox.post({
url: API.checkOrder,
data: urlParams,
isDefaultGet: true,
failMsg: "checkOrder fail"
})
.then(res2 => {
console.log("checkOrder suc: ", res)
return {
payRes:res,
checkRes:res2
}
})
},
handle_clickVipCard() {
util.showNone("储值卡支付未接入")
// this.paySuccess = true
this.handle_clickVIP()
// util.routeTo(`/pages/pay/success`, 'nT');
},
handle_closeEndPhoneBox(val) {
console.log("handle_closeEndPhoneBox", val)
this.showEndPhoneBox = !this.showEndPhoneBox
},
handle_clickCommit_end_phone(val) {
console.log("handle_clickCommit_end_phone", val)
this.showEndPhoneBox = !this.showEndPhoneBox
},
handle_closeQRInputBox(val) {
console.log("handle_closeQRInputBox", val)
},
handle_clickCommit_qr(val) {
console.log("handle_clickCommit_qr", val)
this.showQRInputBox = !this.showQRInputBox
},
handle_clickVIP() {
// util.showNone("会员登录")
this.showEndPhoneBox = !this.showEndPhoneBox
},
handle_clickOpenDoor(e){
console.log("执行开门")
this.openDoorReq()
},
async setPageHeight(){
this.screenHeight = await util.getPageHeight()
},
// 按键松开事件
async onKeyup(event) {
let res = dictKeyboard.dict[event.keyCode];
this.decryptList.push(res);
if(event.keyCode != 13) {
return console.log("this.decryptList1: ",this.decryptList)
};
this.decryptList.pop();
this.strResult = await dictKeyboard.resultScan(this.decryptList);
this.orderQrcode = this.strResult
// console.log("扫码结果--:", this.strResult)
this.decryptList.splice(0);
// console.log("this.decryptList: "+this.decryptList)
//过滤结果中的down_arrow
if(this.strResult.indexOf("down_arrow") != -1){
this.strResult = this.strResult.replace("down_arrow","")
}
console.log("扫码最终结果--:", this.strResult)
this.acceptScanCode(this.strResult)
},
// 监听按键按下事件
onKeydown(event) {
// console.log("监听按键按下事件: ",event)
},
async openDoorReq(){
util.showLoad()
let {store_id,brand_id,token} = getApp().globalData.accountInfo
let picker_door = uni.getStorageSync("picker_door")
let gate_id = picker_door.p_obj_arr[picker_door.p_index].id
let urlParams = {
token,gate_id
}
console.log("openDoor: ",urlParams)
return ox.post({
url: API.openDoor,
data: urlParams,
isDefaultGet: true,
failMsg: "openDoor fail"
})
.then(res => {
util.hideLoad()
console.log("openDoor suc: ", res)
// util.showNone("门禁:开")
util.showModalMsg(`开启门禁执行后, 请耐心等待门禁开启. 请不要重复发起开门指令.`,true)
}).finally(()=>{
util.hideLoad()
})
},
}
}
</script>
<style lang="scss" scoped>
.pay-page {
width: 750rpx;
.mheader{
// width: 750rpx;
width:100%;
}
}
</style>