Browse Source

对接 押金开台(1)、余额开台(2)、定时开台(3)、信用开台(4)、团购券开台(5)、\n立即关台(6)、续费(7)、关灯重开(8)、替人付费关台(9) 开台接口

master
DU 4 months ago
parent
commit
36bab26d4c
  1. 191
      src/api/startDevice.ts
  2. 116
      src/pageScan/closeTable/components/FooterButtons.tsx
  3. 25
      src/pageScan/closeTable/index.tsx
  4. 125
      src/pageScan/couponStart/index.tsx
  5. 115
      src/pageScan/renewFee/index.tsx
  6. 113
      src/pageScan/selfStart/index.tsx
  7. 119
      src/pageScan/timedStart/index.tsx
  8. 127
      src/pageScan/turnOffRestart/index.tsx
  9. 3
      src/store/index.ts
  10. 125
      src/store/table.ts
  11. 150
      src/types/startDevice.ts

191
src/api/startDevice.ts

@ -0,0 +1,191 @@ @@ -0,0 +1,191 @@
/**
* API
* /api/v2/TablesDev/start_device
*/
import { post } from '../services/request'
import {
StartGroupDevRequestDto,
StartDeviceResponse,
WechatPayParameters,
CtrlType,
PayType,
} from '../types/startDevice'
/**
*
* @param data
* @returns
*/
export const startDevice = (data: StartGroupDevRequestDto) => {
return post<StartDeviceResponse>('/TablesDev/start_device', data, {
showLoading: true,
loadingText: '处理中...',
})
}
/**
*
* @param payParams
* @returns Promise
*/
export const invokeWechatPay = (payParams: WechatPayParameters): Promise<void> => {
return new Promise((resolve, reject) => {
wx.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType as 'MD5' | 'HMAC-SHA256' | 'RSA',
paySign: payParams.paySign,
success: () => {
resolve()
},
fail: (err) => {
reject(err)
},
})
})
}
/**
*
* @param grpSn
* @param openId OpenId
* @param amount
* @param payType
*/
export const depositStart = (
grpSn: string,
openId: string,
amount: number,
payType: PayType = PayType.WechatPay
) => {
return startDevice({
grpSn,
openId,
ctrlType: CtrlType.DepositStart,
amount,
payType,
})
}
/**
*
* @param grpSn
* @param openId OpenId
* @param amount
*/
export const balanceStart = (grpSn: string, openId: string, amount: number) => {
return startDevice({
grpSn,
openId,
ctrlType: CtrlType.BalanceStart,
amount,
payType: PayType.Balance,
})
}
/**
*
* @param grpSn
* @param openId OpenId
* @param durationHours
* @param amount
* @param payType
*/
export const timedStart = (
grpSn: string,
openId: string,
durationHours: number,
amount: number,
payType: PayType = PayType.WechatPay
) => {
return startDevice({
grpSn,
openId,
ctrlType: CtrlType.TimedStart,
durationHours,
amount,
payType,
})
}
/**
*
* @param grpSn
* @param openId OpenId
* @param couponCode
*/
export const couponStart = (grpSn: string, openId: string, couponCode: string) => {
return startDevice({
grpSn,
openId,
ctrlType: CtrlType.CouponStart,
couponCode,
})
}
/**
*
* @param grpSn
* @param openId OpenId
*/
export const immediateClose = (grpSn: string, openId: string) => {
return startDevice({
grpSn,
openId,
ctrlType: CtrlType.ImmediateClose,
})
}
/**
*
* @param grpSn
* @param openId OpenId
* @param amount
* @param payType
*/
export const renewFee = (
grpSn: string,
openId: string,
amount: number,
payType: PayType = PayType.WechatPay
) => {
return startDevice({
grpSn,
openId,
ctrlType: CtrlType.Renew,
amount,
payType,
})
}
/**
*
* @param grpSn
* @param openId OpenId
*/
export const turnOffRestart = (grpSn: string, openId: string) => {
return startDevice({
grpSn,
openId,
ctrlType: CtrlType.TurnOffRestart,
})
}
// 导出枚举类型
export { CtrlType, PayType }
// 导出所有方法和类型
export default {
startDevice,
invokeWechatPay,
depositStart,
balanceStart,
timedStart,
couponStart,
immediateClose,
renewFee,
turnOffRestart,
CtrlType,
PayType,
}

116
src/pageScan/closeTable/components/FooterButtons.tsx

@ -3,29 +3,129 @@ @@ -3,29 +3,129 @@
*/
import { View, Text } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { useState } from 'react'
import { immediateClose, invokeWechatPay } from '../../../api/startDevice'
import { userStore } from '../../../store'
import './FooterButtons.scss'
interface FooterButtonsProps {
type: 'owner' | 'guest' // owner: 开台人员扫码, guest: 非开台人扫码
grpSn: string // 设备编码
operationId: number // 操作记录ID
}
const FooterButtons = ({ type }: FooterButtonsProps) => {
const FooterButtons = ({ type, grpSn, operationId }: FooterButtonsProps) => {
const [loading, setLoading] = useState(false)
// 立即关台
const handleCloseNow = () => {
console.log('立即关台')
const handleCloseNow = async () => {
if (loading) return
// 获取用户 openId
const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
if (!openId) {
Taro.showToast({
title: '功能开发中',
title: '请先登录',
icon: 'none',
duration: 2000
})
return
}
if (!grpSn) {
Taro.showToast({
title: '设备信息异常',
icon: 'none',
duration: 2000
})
return
}
// 确认关台
const confirmResult = await Taro.showModal({
title: '确认关台',
content: '确定要立即关台吗?',
confirmText: '确定',
cancelText: '取消'
})
if (!confirmResult.confirm) {
return
}
setLoading(true)
try {
console.log('立即关台,设备编码:', grpSn, '操作记录ID:', operationId)
// 调用立即关台接口
const result = await immediateClose(grpSn, openId)
console.log('关台接口返回:', result)
// 判断是否需要支付(可能需要补差价)
if (result.data.payParameters) {
// 调起微信支付
try {
await invokeWechatPay(result.data.payParameters)
// 支付成功
Taro.showToast({
title: '关台成功',
icon: 'success',
duration: 2000
})
// 跳转到订单详情或首页
setTimeout(() => {
Taro.switchTab({
url: '/pages/index/index'
})
}, 2000)
} catch (payError: any) {
console.log('支付错误:', payError)
// 用户取消支付或支付失败
if (payError.errMsg?.includes('cancel')) {
Taro.showToast({
title: '已取消支付',
icon: 'none',
duration: 2000
})
} else {
Taro.showToast({
title: payError.errMsg || '支付失败',
icon: 'none',
duration: 2000
})
}
}
} else {
// 无需支付
Taro.showToast({
title: '关台成功',
icon: 'success',
duration: 2000
})
setTimeout(() => {
Taro.switchTab({
url: '/pages/index/index'
})
}, 2000)
}
} catch (error: any) {
console.error('关台失败:', error)
// 错误提示由 request 拦截器处理
} finally {
setLoading(false)
}
}
// 续费
const handleRecharge = () => {
console.log('续费')
Taro.navigateTo({
url: '/pageScan/renewFee/index'
url: `/pageScan/renewFee/index?grpSn=${grpSn}&operationId=${operationId}`
})
}
@ -63,7 +163,7 @@ const FooterButtons = ({ type }: FooterButtonsProps) => { @@ -63,7 +163,7 @@ const FooterButtons = ({ type }: FooterButtonsProps) => {
const handleTurnOffRestart = () => {
console.log('关灯重开')
Taro.navigateTo({
url: '/pageScan/turnOffRestart/index'
url: `/pageScan/turnOffRestart/index?grpSn=${grpSn}&operationId=${operationId}`
})
}
@ -72,8 +172,8 @@ const FooterButtons = ({ type }: FooterButtonsProps) => { @@ -72,8 +172,8 @@ const FooterButtons = ({ type }: FooterButtonsProps) => {
{type === 'owner' ? (
<>
{/* 开台人员扫码 - 图1 */}
<View className="primary-button green" onClick={handleCloseNow}>
<Text className="button-text"></Text>
<View className={`primary-button green ${loading ? 'disabled' : ''}`} onClick={handleCloseNow}>
<Text className="button-text">{loading ? '处理中...' : '立即关台'}</Text>
</View>
<View className="primary-button green" onClick={handleRecharge}>
<Text className="button-text"></Text>

25
src/pageScan/closeTable/index.tsx

@ -4,23 +4,40 @@ @@ -4,23 +4,40 @@
import { View, Text, Image } from '@tarojs/components'
import Taro, { useLoad, useRouter } from '@tarojs/taro'
import { useState } from 'react'
import { observer } from 'mobx-react'
import { ArrowDown } from '@taroify/icons'
import FooterButtons from './components/FooterButtons'
import { tableStore } from '../../store'
import './index.scss'
// 使用本地图片资源
const phoneIcon = require('../startTable/images/phone.png')
const locationIcon = require('../../assets/icon/locationIcon.png')
const CloseTable = () => {
const CloseTable = observer(() => {
const router = useRouter()
const { type = 'owner' } = router.params as { type?: 'owner' | 'guest' } // owner: 开台人员, guest: 非开台人
const [expanded, setExpanded] = useState(false) // 订单详情是否展开
const [couponExpanded, setCouponExpanded] = useState(false) // 优惠券详情是否展开
const [priceExpanded, setPriceExpanded] = useState(false) // 收费标准详情是否展开
// 从路由参数获取设备信息和操作记录ID,如果没有则从 store 获取
const grpSn = tableStore.getGrpSn(router.params.grpSn)
const operationId = tableStore.getOperationId(Number(router.params.operationId))
useLoad(() => {
console.log('关台页面加载,类型:', type)
console.log('关台页面加载,类型:', type, '设备编码:', grpSn, '操作记录ID:', operationId)
// 保存到 store
if (grpSn) {
tableStore.setCurrentGrpSn(grpSn)
}
if (operationId) {
tableStore.setCurrentOperationId(operationId)
}
// TODO: 这里应该调用接口获取订单详情
// 根据 grpSn 和 operationId 获取订单信息
})
// 拨打电话
@ -225,9 +242,9 @@ const CloseTable = () => { @@ -225,9 +242,9 @@ const CloseTable = () => {
</View>
{/* 底部按钮区域 */}
<FooterButtons type={type} />
<FooterButtons type={type} grpSn={grpSn} operationId={operationId} />
</View>
)
}
})
export default CloseTable

125
src/pageScan/couponStart/index.tsx

@ -2,8 +2,11 @@ @@ -2,8 +2,11 @@
*
*/
import { View, Text, Image, Input } from '@tarojs/components'
import Taro, { useLoad } from '@tarojs/taro'
import Taro, { useLoad, useRouter } from '@tarojs/taro'
import { useState } from 'react'
import { observer } from 'mobx-react'
import { couponStart, invokeWechatPay } from '../../api/startDevice'
import { userStore, tableStore } from '../../store'
import './index.scss'
// 使用本地图片资源
@ -29,10 +32,15 @@ interface Coupon { @@ -29,10 +32,15 @@ interface Coupon {
expanded: boolean
}
const CouponStart = () => {
const CouponStart = observer(() => {
const router = useRouter()
const [couponCode, setCouponCode] = useState('') // 券码
const [selectedCouponType, setSelectedCouponType] = useState<CouponType>(CouponType.THIRD_PARTY)
const [selectedCouponId, setSelectedCouponId] = useState<number>(1) // 选中的券ID
const [loading, setLoading] = useState(false) // 加载状态
// 从路由参数获取设备信息,如果没有则从 store 获取
const grpSn = tableStore.getGrpSn(router.params.grpSn)
// 券列表数据
const [coupons, setCoupons] = useState<Coupon[]>([
@ -61,7 +69,12 @@ const CouponStart = () => { @@ -61,7 +69,12 @@ const CouponStart = () => {
])
useLoad(() => {
console.log('团购券开台页面加载')
console.log('团购券开台页面加载, 设备编码:', grpSn)
// 保存设备编码到 store
if (grpSn) {
tableStore.setCurrentGrpSn(grpSn)
}
})
// 拨打电话
@ -109,23 +122,113 @@ const CouponStart = () => { @@ -109,23 +122,113 @@ const CouponStart = () => {
}
// 兑换开台
const handleExchange = () => {
if (!selectedCouponId) {
const handleExchange = async () => {
if (loading) return
// 验证券码
if (!couponCode && !selectedCouponId) {
Taro.showToast({
title: '请输入券码或选择一张券',
icon: 'none',
duration: 2000
})
return
}
// 获取用户 openId
const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
if (!openId) {
Taro.showToast({
title: '请选择一张券',
title: '请先登录',
icon: 'none',
duration: 2000
})
return
}
console.log('兑换开台,选中券ID:', selectedCouponId)
if (!grpSn) {
Taro.showToast({
title: '设备信息异常,请重新扫码',
icon: 'none',
duration: 2000
})
return
}
setLoading(true)
try {
// 使用输入的券码或选中的券
const finalCouponCode = couponCode || `COUPON_${selectedCouponId}`
console.log('团购券开台,券码:', finalCouponCode, '设备编码:', grpSn)
// 调用团购券开台接口
const result = await couponStart(grpSn, openId, finalCouponCode)
console.log('开台接口返回:', result)
// 保存操作记录ID到 store
if (result.data.consumptionId) {
tableStore.setCurrentOperationId(result.data.consumptionId)
}
// 判断是否需要支付(团购券一般不需要支付,但可能需要补差价)
if (result.data.payParameters) {
// 调起微信支付
try {
await invokeWechatPay(result.data.payParameters)
// 支付成功
Taro.showToast({
title: '开台成功',
icon: 'success',
duration: 2000
})
// 跳转到关台页面
setTimeout(() => {
Taro.redirectTo({
url: `/pageScan/closeTable/index?grpSn=${grpSn}&operationId=${result.data.consumptionId}`
})
}, 2000)
} catch (payError: any) {
console.log('支付错误:', payError)
// 用户取消支付或支付失败
if (payError.errMsg?.includes('cancel')) {
Taro.showToast({
title: '已取消支付',
icon: 'none',
duration: 2000
})
} else {
Taro.showToast({
title: '功能开发中',
title: payError.errMsg || '支付失败',
icon: 'none',
duration: 2000
})
}
}
} else {
// 无需支付
Taro.showToast({
title: '兑换成功,开台成功',
icon: 'success',
duration: 2000
})
setTimeout(() => {
Taro.redirectTo({
url: `/pageScan/closeTable/index?grpSn=${grpSn}&operationId=${result.data.consumptionId}`
})
}, 2000)
}
} catch (error: any) {
console.error('兑换开台失败:', error)
// 错误提示由 request 拦截器处理
} finally {
setLoading(false)
}
}
return (
<View className="coupon-start-page">
@ -303,12 +406,12 @@ const CouponStart = () => { @@ -303,12 +406,12 @@ const CouponStart = () => {
{/* 底部固定按钮 */}
<View className="footer-bar">
<View className="exchange-btn" onClick={handleExchange}>
<Text className="exchange-text"></Text>
<View className={`exchange-btn ${loading ? 'disabled' : ''}`} onClick={handleExchange}>
<Text className="exchange-text">{loading ? '处理中...' : '兑换开台'}</Text>
</View>
</View>
</View>
)
}
})
export default CouponStart

115
src/pageScan/renewFee/index.tsx

@ -2,21 +2,38 @@ @@ -2,21 +2,38 @@
*
*/
import { View, Text, Image } from '@tarojs/components'
import Taro, { useLoad } from '@tarojs/taro'
import Taro, { useLoad, useRouter } from '@tarojs/taro'
import { useState } from 'react'
import { observer } from 'mobx-react'
import { Stepper } from '@taroify/core'
import { renewFee, invokeWechatPay, PayType } from '../../api/startDevice'
import { userStore, tableStore } from '../../store'
import './index.scss'
// 使用本地图片资源
const phoneIcon = require('../startTable/images/phone.png')
const locationIcon = require('../../assets/icon/locationIcon.png')
const RenewFee = () => {
const [renewAmount, setRenewAmount] = useState(1000) // 续费金额
const RenewFee = observer(() => {
const router = useRouter()
const [renewAmount, setRenewAmount] = useState(100) // 续费金额(修改初始值为100)
const [paymentMethod] = useState('微信支付') // 支付方式
const [loading, setLoading] = useState(false) // 加载状态
// 从路由参数获取设备信息和操作记录ID,如果没有则从 store 获取
const grpSn = tableStore.getGrpSn(router.params.grpSn)
const operationId = tableStore.getOperationId(Number(router.params.operationId))
useLoad(() => {
console.log('续费页面加载')
console.log('续费页面加载, 设备编码:', grpSn, '操作记录ID:', operationId)
// 保存到 store
if (grpSn) {
tableStore.setCurrentGrpSn(grpSn)
}
if (operationId) {
tableStore.setCurrentOperationId(operationId)
}
})
// 拨打电话
@ -50,13 +67,91 @@ const RenewFee = () => { @@ -50,13 +67,91 @@ const RenewFee = () => {
}
// 立即续费
const handleConfirmRenew = () => {
console.log('立即续费,金额:', renewAmount)
const handleConfirmRenew = async () => {
if (loading) return
// 获取用户 openId
const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
if (!openId) {
Taro.showToast({
title: '请先登录',
icon: 'none',
duration: 2000
})
return
}
if (!grpSn) {
Taro.showToast({
title: '设备信息异常',
icon: 'none',
duration: 2000
})
return
}
setLoading(true)
try {
console.log('立即续费,金额:', renewAmount, '设备编码:', grpSn)
// 调用续费接口
const result = await renewFee(grpSn, openId, renewAmount, PayType.WechatPay)
console.log('续费接口返回:', result)
// 判断是否需要支付
if (result.data.payParameters) {
// 调起微信支付
try {
await invokeWechatPay(result.data.payParameters)
// 支付成功
Taro.showToast({
title: '续费成功',
icon: 'success',
duration: 2000
})
// 返回关台页面
setTimeout(() => {
Taro.navigateBack()
}, 2000)
} catch (payError: any) {
console.log('支付错误:', payError)
// 用户取消支付或支付失败
if (payError.errMsg?.includes('cancel')) {
Taro.showToast({
title: '功能开发中',
title: '已取消支付',
icon: 'none',
duration: 2000
})
} else {
Taro.showToast({
title: payError.errMsg || '支付失败',
icon: 'none',
duration: 2000
})
}
}
} else {
// 无需支付
Taro.showToast({
title: '续费成功',
icon: 'success',
duration: 2000
})
setTimeout(() => {
Taro.navigateBack()
}, 2000)
}
} catch (error: any) {
console.error('续费失败:', error)
// 错误提示由 request 拦截器处理
} finally {
setLoading(false)
}
}
return (
@ -172,12 +267,12 @@ const RenewFee = () => { @@ -172,12 +267,12 @@ const RenewFee = () => {
<Text className="payment-symbol"></Text>
<Text className="payment-amount">{renewAmount.toFixed(2)}</Text>
</View>
<View className="confirm-btn" onClick={handleConfirmRenew}>
<Text className="confirm-text"></Text>
<View className={`confirm-btn ${loading ? 'disabled' : ''}`} onClick={handleConfirmRenew}>
<Text className="confirm-text">{loading ? '处理中...' : '立即续费'}</Text>
</View>
</View>
</View>
)
}
})
export default RenewFee

113
src/pageScan/selfStart/index.tsx

@ -4,26 +4,35 @@ @@ -4,26 +4,35 @@
import { View, Text, Image } from '@tarojs/components'
import Taro, { useLoad, useRouter } from '@tarojs/taro'
import { useState } from 'react'
import { observer } from 'mobx-react'
import { Stepper } from '@taroify/core'
import { startTable, invokeWechatPay, TableOperationType, PaymentChannel } from '../../services/tableApi'
import { depositStart, balanceStart, invokeWechatPay, PayType } from '../../api/startDevice'
import { userStore, tableStore } from '../../store'
import './index.scss'
// 使用本地图片资源
const phoneIcon = require('../startTable/images/phone.png')
const SelfStart = () => {
const SelfStart = observer(() => {
const router = useRouter()
const [deposit, setDeposit] = useState(100) // 押金金额
const [paymentMethod] = useState('微信支付') // 支付方式
const [storeBalance] = useState(20.00) // 门店余额
const [paymentMethod, setPaymentMethod] = useState<'微信支付' | '余额支付'>('微信支付') // 支付方式
const [loading, setLoading] = useState(false) // 加载状态
// 从路由参数获取设备信息
const grpSn = router.params.grpSn || '' // 设备编码
// 从路由参数获取设备信息,如果没有则从 store 获取
const grpSn = tableStore.getGrpSn(router.params.grpSn)
// 门店余额从 userStore 获取
const storeBalance = userStore.balance || 0
useLoad(() => {
console.log('自助开台页面加载, 设备编码:', grpSn)
// 保存设备编码到 store
if (grpSn) {
tableStore.setCurrentGrpSn(grpSn)
}
// 显示提示 Toast
setTimeout(() => {
Taro.showToast({
@ -57,12 +66,26 @@ const SelfStart = () => { @@ -57,12 +66,26 @@ const SelfStart = () => {
// 选择支付方式
const handleSelectPayment = () => {
// 判断余额是否充足
if (storeBalance >= deposit) {
Taro.showActionSheet({
itemList: ['微信支付', '余额支付'],
success: (res) => {
if (res.tapIndex === 0) {
setPaymentMethod('微信支付')
} else if (res.tapIndex === 1) {
setPaymentMethod('余额支付')
}
}
})
} else {
Taro.showToast({
title: '暂时只支持微信支付',
title: '余额不足,仅支持微信支付',
icon: 'none',
duration: 2000
})
}
}
// 去充值
const handleRecharge = () => {
@ -79,46 +102,47 @@ const SelfStart = () => { @@ -79,46 +102,47 @@ const SelfStart = () => {
if (loading) return
// 获取用户 openId
// const openId = Taro.getStorageSync('openId')
// if (!openId) {
// Taro.showToast({
// title: '请先登录',
// icon: 'none',
// duration: 2000
// })
// return
// }
// if (!grpSn) {
// Taro.showToast({
// title: '设备信息异常,请重新扫码',
// icon: 'none',
// duration: 2000
// })
// return
// }
const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
if (!openId) {
Taro.showToast({
title: '请先登录',
icon: 'none',
duration: 2000
})
return
}
if (!grpSn) {
Taro.showToast({
title: '设备信息异常,请重新扫码',
icon: 'none',
duration: 2000
})
return
}
setLoading(true)
try {
console.log('立即开台,押金:', deposit, '设备编码:', grpSn)
// TODO: 暂时写假的测试下
const params = {
grpSn:'123',
openId:'4221',
ctrlType: 1, // TableOperationType.SelfServiceDeposit, // 押金开台
amount: 100,//deposit,
payType: 1,//PaymentChannel.WechatPay, // 微信支付
durationHours: 3,
couponCode: '',
console.log('立即开台,押金:', deposit, '设备编码:', grpSn, '支付方式:', paymentMethod)
// 根据支付方式选择接口
let result
if (paymentMethod === '余额支付') {
// 余额开台
result = await balanceStart(grpSn, openId, deposit)
} else {
// 押金开台(微信支付)
result = await depositStart(grpSn, openId, deposit, PayType.WechatPay)
}
console.log('开台请求参数:', params)
// 调用开台接口
const result = await startTable(params)
console.log('开台接口返回:', result)
// 保存操作记录ID到 store
if (result.data.consumptionId) {
tableStore.setCurrentOperationId(result.data.consumptionId)
}
// 判断是否需要支付
if (result.data.payParameters) {
// 调起微信支付
@ -132,15 +156,14 @@ const SelfStart = () => { @@ -132,15 +156,14 @@ const SelfStart = () => {
duration: 2000
})
// 跳转到关台页面或开台成功页面
// 跳转到关台页面
setTimeout(() => {
Taro.redirectTo({
url: `/pageScan/closeTable/index?grpSn=${grpSn}&operationId=${result.data.operationId}`
url: `/pageScan/closeTable/index?grpSn=${grpSn}&operationId=${result.data.consumptionId}`
})
}, 2000)
} catch (payError: any) {
console.log('error:', payError)
console.log('支付错误:', payError)
// 用户取消支付或支付失败
if (payError.errMsg?.includes('cancel')) {
Taro.showToast({
@ -166,7 +189,7 @@ const SelfStart = () => { @@ -166,7 +189,7 @@ const SelfStart = () => {
setTimeout(() => {
Taro.redirectTo({
url: `/pageScan/closeTable/index?grpSn=${grpSn}&operationId=${result.data.operationId}`
url: `/pageScan/closeTable/index?grpSn=${grpSn}&operationId=${result.data.consumptionId}`
})
}, 2000)
}
@ -285,6 +308,6 @@ const SelfStart = () => { @@ -285,6 +308,6 @@ const SelfStart = () => {
</View>
</View>
)
}
})
export default SelfStart

119
src/pageScan/timedStart/index.tsx

@ -2,8 +2,11 @@ @@ -2,8 +2,11 @@
*
*/
import { View, Text, Image } from '@tarojs/components'
import Taro, { useLoad } from '@tarojs/taro'
import Taro, { useLoad, useRouter } from '@tarojs/taro'
import { useState } from 'react'
import { observer } from 'mobx-react'
import { timedStart, invokeWechatPay, PayType } from '../../api/startDevice'
import { userStore, tableStore } from '../../store'
import './index.scss'
// 使用本地图片资源
@ -21,12 +24,22 @@ const durationOptions = [ @@ -21,12 +24,22 @@ const durationOptions = [
{ id: 8, label: '8小时', value: 8 },
]
const TimedStart = () => {
const TimedStart = observer(() => {
const router = useRouter()
const [selectedDuration, setSelectedDuration] = useState(1) // 选中的时长
const [paymentMethod] = useState('微信支付') // 支付方式
const [loading, setLoading] = useState(false) // 加载状态
// 从路由参数获取设备信息,如果没有则从 store 获取
const grpSn = tableStore.getGrpSn(router.params.grpSn)
useLoad(() => {
console.log('定时开台页面加载')
console.log('定时开台页面加载, 设备编码:', grpSn)
// 保存设备编码到 store
if (grpSn) {
tableStore.setCurrentGrpSn(grpSn)
}
})
// 拨打电话
@ -58,13 +71,101 @@ const TimedStart = () => { @@ -58,13 +71,101 @@ const TimedStart = () => {
}
// 立即开台
const handleConfirmStart = () => {
console.log('立即开台,时长:', selectedDuration, '小时')
const handleConfirmStart = async () => {
if (loading) return
// 获取用户 openId
const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
if (!openId) {
Taro.showToast({
title: '功能开发中',
title: '请先登录',
icon: 'none',
duration: 2000
})
return
}
if (!grpSn) {
Taro.showToast({
title: '设备信息异常,请重新扫码',
icon: 'none',
duration: 2000
})
return
}
setLoading(true)
try {
const amount = parseFloat(calculateAmount())
console.log('定时开台,时长:', selectedDuration, '小时,金额:', amount, '设备编码:', grpSn)
// 调用定时开台接口
const result = await timedStart(grpSn, openId, selectedDuration, amount, PayType.WechatPay)
console.log('开台接口返回:', result)
// 保存操作记录ID到 store
if (result.data.consumptionId) {
tableStore.setCurrentOperationId(result.data.consumptionId)
}
// 判断是否需要支付
if (result.data.payParameters) {
// 调起微信支付
try {
await invokeWechatPay(result.data.payParameters)
// 支付成功
Taro.showToast({
title: '开台成功',
icon: 'success',
duration: 2000
})
// 跳转到关台页面
setTimeout(() => {
Taro.redirectTo({
url: `/pageScan/closeTable/index?grpSn=${grpSn}&operationId=${result.data.consumptionId}`
})
}, 2000)
} catch (payError: any) {
console.log('支付错误:', payError)
// 用户取消支付或支付失败
if (payError.errMsg?.includes('cancel')) {
Taro.showToast({
title: '已取消支付',
icon: 'none',
duration: 2000
})
} else {
Taro.showToast({
title: payError.errMsg || '支付失败',
icon: 'none',
duration: 2000
})
}
}
} else {
// 无需支付
Taro.showToast({
title: '开台成功',
icon: 'success',
duration: 2000
})
setTimeout(() => {
Taro.redirectTo({
url: `/pageScan/closeTable/index?grpSn=${grpSn}&operationId=${result.data.consumptionId}`
})
}, 2000)
}
} catch (error: any) {
console.error('开台失败:', error)
// 错误提示由 request 拦截器处理
} finally {
setLoading(false)
}
}
return (
@ -146,12 +247,12 @@ const TimedStart = () => { @@ -146,12 +247,12 @@ const TimedStart = () => {
<Text className="payment-symbol"></Text>
<Text className="payment-amount">{calculateAmount()}</Text>
</View>
<View className="confirm-btn" onClick={handleConfirmStart}>
<Text className="confirm-text"></Text>
<View className={`confirm-btn ${loading ? 'disabled' : ''}`} onClick={handleConfirmStart}>
<Text className="confirm-text">{loading ? '处理中...' : '立即开台'}</Text>
</View>
</View>
</View>
)
}
})
export default TimedStart

127
src/pageScan/turnOffRestart/index.tsx

@ -2,16 +2,35 @@ @@ -2,16 +2,35 @@
*
*/
import { View, Text, Image } from '@tarojs/components'
import Taro, { useLoad } from '@tarojs/taro'
import Taro, { useLoad, useRouter } from '@tarojs/taro'
import { useState } from 'react'
import { observer } from 'mobx-react'
import { turnOffRestart, invokeWechatPay } from '../../api/startDevice'
import { userStore, tableStore } from '../../store'
import './index.scss'
// 使用本地图片资源
const phoneIcon = require('../startTable/images/phone.png')
const locationIcon = require('../../assets/icon/locationIcon.png')
const TurnOffRestart = () => {
const TurnOffRestart = observer(() => {
const router = useRouter()
const [loading, setLoading] = useState(false) // 加载状态
// 从路由参数获取设备信息和操作记录ID,如果没有则从 store 获取
const grpSn = tableStore.getGrpSn(router.params.grpSn)
const operationId = tableStore.getOperationId(Number(router.params.operationId))
useLoad(() => {
console.log('关灯重开页面加载')
console.log('关灯重开页面加载, 设备编码:', grpSn, '操作记录ID:', operationId)
// 保存到 store
if (grpSn) {
tableStore.setCurrentGrpSn(grpSn)
}
if (operationId) {
tableStore.setCurrentOperationId(operationId)
}
})
// 拨打电话
@ -24,14 +43,104 @@ const TurnOffRestart = () => { @@ -24,14 +43,104 @@ const TurnOffRestart = () => {
}
// 确认结算
const handleConfirmSettle = () => {
console.log('确认结算')
const handleConfirmSettle = async () => {
if (loading) return
// 获取用户 openId
const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
if (!openId) {
Taro.showToast({
title: '请先登录',
icon: 'none',
duration: 2000
})
return
}
if (!grpSn) {
Taro.showToast({
title: '设备信息异常',
icon: 'none',
duration: 2000
})
return
}
// 确认结算
const confirmResult = await Taro.showModal({
title: '确认结算',
content: '结算本次消费金额,押金于最后关台时结算。确定要继续吗?',
confirmText: '确定',
cancelText: '取消'
})
if (!confirmResult.confirm) {
return
}
setLoading(true)
try {
console.log('关灯重开,设备编码:', grpSn, '操作记录ID:', operationId)
// 调用关灯重开接口
const result = await turnOffRestart(grpSn, openId)
console.log('关灯重开接口返回:', result)
// 判断是否需要支付
if (result.data.payParameters) {
// 调起微信支付
try {
await invokeWechatPay(result.data.payParameters)
// 支付成功
Taro.showToast({
title: '结算成功',
icon: 'success',
duration: 2000
})
// 返回关台页面或跳转到首页
setTimeout(() => {
Taro.navigateBack()
}, 2000)
} catch (payError: any) {
console.log('支付错误:', payError)
// 用户取消支付或支付失败
if (payError.errMsg?.includes('cancel')) {
Taro.showToast({
title: '已取消支付',
icon: 'none',
duration: 2000
})
} else {
Taro.showToast({
title: '功能开发中',
title: payError.errMsg || '支付失败',
icon: 'none',
duration: 2000
})
}
}
} else {
// 无需支付
Taro.showToast({
title: '结算成功',
icon: 'success',
duration: 2000
})
setTimeout(() => {
Taro.navigateBack()
}, 2000)
}
} catch (error: any) {
console.error('关灯重开失败:', error)
// 错误提示由 request 拦截器处理
} finally {
setLoading(false)
}
}
return (
<View className="turn-off-restart-page">
@ -109,12 +218,12 @@ const TurnOffRestart = () => { @@ -109,12 +218,12 @@ const TurnOffRestart = () => {
{/* 底部固定区域 */}
<View className="footer-bar">
<View className="confirm-btn" onClick={handleConfirmSettle}>
<Text className="confirm-text"></Text>
<View className={`confirm-btn ${loading ? 'disabled' : ''}`} onClick={handleConfirmSettle}>
<Text className="confirm-text">{loading ? '处理中...' : '确认结算'}</Text>
</View>
</View>
</View>
)
}
})
export default TurnOffRestart

3
src/store/index.ts

@ -3,13 +3,16 @@ @@ -3,13 +3,16 @@
*/
import { userStore as _userStore } from './user'
import { appStore as _appStore } from './app'
import { tableStore as _tableStore } from './table'
// 重新导出
export const userStore = _userStore
export const appStore = _appStore
export const tableStore = _tableStore
// 导出所有 store 的集合
export const stores = {
userStore: _userStore,
appStore: _appStore,
tableStore: _tableStore,
}

125
src/store/table.ts

@ -0,0 +1,125 @@ @@ -0,0 +1,125 @@
/**
* - MobX
*/
import { makeAutoObservable } from 'mobx'
import Taro from '@tarojs/taro'
import { TableInfo, OrderInfo } from '../types/startDevice'
class TableStore {
/** 当前设备编码 */
currentGrpSn: string = '123'
/** 当前操作记录ID */
currentOperationId: number = 0
/** 台球桌信息 */
tableInfo: TableInfo | null = null
/** 订单信息 */
orderInfo: OrderInfo | null = null
/** 加载状态 */
isLoading: boolean = false
constructor() {
makeAutoObservable(this)
this.initFromStorage()
}
/**
*
*/
initFromStorage = () => {
try {
const grpSn = Taro.getStorageSync('currentGrpSn')
const operationId = Taro.getStorageSync('currentOperationId')
const tableInfo = Taro.getStorageSync('tableInfo')
const orderInfo = Taro.getStorageSync('orderInfo')
if (grpSn) this.currentGrpSn = grpSn
if (operationId) this.currentOperationId = operationId
if (tableInfo) this.tableInfo = tableInfo
if (orderInfo) this.orderInfo = orderInfo
} catch (error) {
console.error('读取台球桌信息失败:', error)
}
}
/**
*
*/
setCurrentGrpSn = (grpSn: string) => {
this.currentGrpSn = grpSn
Taro.setStorageSync('currentGrpSn', grpSn)
}
/**
* ID
*/
setCurrentOperationId = (operationId: number) => {
this.currentOperationId = operationId
Taro.setStorageSync('currentOperationId', operationId)
}
/**
*
*/
setTableInfo = (info: TableInfo | null) => {
this.tableInfo = info
if (info) {
Taro.setStorageSync('tableInfo', info)
} else {
Taro.removeStorageSync('tableInfo')
}
}
/**
*
*/
setOrderInfo = (info: OrderInfo | null) => {
this.orderInfo = info
if (info) {
Taro.setStorageSync('orderInfo', info)
} else {
Taro.removeStorageSync('orderInfo')
}
}
/**
*
*/
setLoading = (loading: boolean) => {
this.isLoading = loading
}
/**
*
*/
clearAll = () => {
this.currentGrpSn = ''
this.currentOperationId = 0
this.tableInfo = null
this.orderInfo = null
Taro.removeStorageSync('currentGrpSn')
Taro.removeStorageSync('currentOperationId')
Taro.removeStorageSync('tableInfo')
Taro.removeStorageSync('orderInfo')
}
/**
* store
*/
getGrpSn = (paramGrpSn?: string): string => {
return paramGrpSn || this.currentGrpSn
}
/**
* IDstore
*/
getOperationId = (paramOperationId?: number): number => {
return paramOperationId || this.currentOperationId
}
}
export const tableStore = new TableStore()

150
src/types/startDevice.ts

@ -0,0 +1,150 @@ @@ -0,0 +1,150 @@
/**
*
* /api/v2/TablesDev/start_device
*/
/**
*
*/
export enum CtrlType {
/** 押金开台 */
DepositStart = 1,
/** 余额开台 */
BalanceStart = 2,
/** 定时开台 */
TimedStart = 3,
/** 信用开台 */
CreditStart = 4,
/** 团购券开台 */
CouponStart = 5,
/** 立即关台 */
ImmediateClose = 6,
/** 续费 */
Renew = 7,
/** 关灯重开 */
TurnOffRestart = 8,
/** 替人付费关台 */
PayForOthersClose = 9,
}
/**
*
*/
export enum PayType {
/** 微信支付 */
WechatPay = 1,
/** 支付宝 */
Alipay = 2,
/** 银联支付 */
UnionPay = 3,
/** 余额支付 */
Balance = 4,
/** 银行卡支付 */
BankCard = 5,
}
/**
*
*/
export interface StartGroupDevRequestDto {
/** 设备编码(必填) */
grpSn: string
/** OpenId(必填) */
openId: string
/** 操作类型:押金开台(1)、余额开台(2)、定时开台(3)、信用开台(4)、团购券开台(5)、立即关台(6)、续费(7)、关灯重开(8)、替人付费关台(9) */
ctrlType?: CtrlType
/** 消费金额 */
amount?: number
/** 支付方式:微信支付(1)、支付宝(2)、银联支付(3)、余额支付(4)、银行卡支付(5) */
payType?: PayType
/** 开台时长(小时),定时开台使用 */
durationHours?: number
/** 团购券码,团购券开台使用 */
couponCode?: string
/** 备注 */
remark?: string
/** 附加数据(JSON格式) */
attach?: string
}
/**
*
*/
export interface WechatPayParameters {
/** 时间戳 */
timeStamp: string
/** 随机字符串 */
nonceStr: string
/** 统一下单接口返回的 prepay_id */
package: string
/** 签名方式 */
signType: string
/** 签名 */
paySign: string
}
/**
*
*/
export interface StartDeviceResponse {
/** 消费记录ID */
consumptionId: number
/** 商户订单号 */
outTradeNo: string
/** 支付参数(用于调起微信支付) */
payParameters: WechatPayParameters | null
/** H5支付URL */
mwebUrl: string
/** 支付URL(用于扫码支付) */
payUrl: string
/** 描述消息 */
errorMessage: string
}
/**
*
*/
export interface TableInfo {
/** 设备编码 */
grpSn: string
/** 台球桌名称 */
tableName: string
/** 台球桌类型 */
tableType: string
/** 门店名称 */
storeName: string
/** 门店电话 */
storePhone: string
/** 价格(元/小时) */
price: number
/** VIP价格(元/小时) */
vipPrice: number
/** 押金金额 */
deposit: number
/** 价格时段 */
pricePeriod: string
}
/**
*
*/
export interface OrderInfo {
/** 操作记录ID */
operationId: number
/** 设备编码 */
grpSn: string
/** 开始时间 */
startTime: string
/** 已消费时长(分钟) */
consumedMinutes: number
/** 已消费金额 */
consumedAmount: number
/** 剩余时长(分钟) */
remainingMinutes?: number
/** 订单编号 */
orderNo: string
/** 开台方式 */
startMethod: string
/** 收费标准 */
pricingRule: string
}
Loading…
Cancel
Save