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. 124
      src/pageScan/closeTable/components/FooterButtons.tsx
  3. 25
      src/pageScan/closeTable/index.tsx
  4. 133
      src/pageScan/couponStart/index.tsx
  5. 123
      src/pageScan/renewFee/index.tsx
  6. 117
      src/pageScan/selfStart/index.tsx
  7. 127
      src/pageScan/timedStart/index.tsx
  8. 133
      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 @@
/**
* 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,
}

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

@ -3,29 +3,129 @@
*/ */
import { View, Text } from '@tarojs/components' import { View, Text } from '@tarojs/components'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import { useState } from 'react'
import { immediateClose, invokeWechatPay } from '../../../api/startDevice'
import { userStore } from '../../../store'
import './FooterButtons.scss' import './FooterButtons.scss'
interface FooterButtonsProps { interface FooterButtonsProps {
type: 'owner' | 'guest' // owner: 开台人员扫码, guest: 非开台人扫码 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 = () => { const handleCloseNow = async () => {
console.log('立即关台') if (loading) return
Taro.showToast({
title: '功能开发中', // 获取用户 openId
icon: 'none', const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
duration: 2000 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 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 = () => { const handleRecharge = () => {
console.log('续费') console.log('续费')
Taro.navigateTo({ Taro.navigateTo({
url: '/pageScan/renewFee/index' url: `/pageScan/renewFee/index?grpSn=${grpSn}&operationId=${operationId}`
}) })
} }
@ -63,7 +163,7 @@ const FooterButtons = ({ type }: FooterButtonsProps) => {
const handleTurnOffRestart = () => { const handleTurnOffRestart = () => {
console.log('关灯重开') console.log('关灯重开')
Taro.navigateTo({ Taro.navigateTo({
url: '/pageScan/turnOffRestart/index' url: `/pageScan/turnOffRestart/index?grpSn=${grpSn}&operationId=${operationId}`
}) })
} }
@ -72,8 +172,8 @@ const FooterButtons = ({ type }: FooterButtonsProps) => {
{type === 'owner' ? ( {type === 'owner' ? (
<> <>
{/* 开台人员扫码 - 图1 */} {/* 开台人员扫码 - 图1 */}
<View className="primary-button green" onClick={handleCloseNow}> <View className={`primary-button green ${loading ? 'disabled' : ''}`} onClick={handleCloseNow}>
<Text className="button-text"></Text> <Text className="button-text">{loading ? '处理中...' : '立即关台'}</Text>
</View> </View>
<View className="primary-button green" onClick={handleRecharge}> <View className="primary-button green" onClick={handleRecharge}>
<Text className="button-text"></Text> <Text className="button-text"></Text>

25
src/pageScan/closeTable/index.tsx

@ -4,23 +4,40 @@
import { View, Text, Image } from '@tarojs/components' import { View, Text, Image } from '@tarojs/components'
import Taro, { useLoad, useRouter } from '@tarojs/taro' import Taro, { useLoad, useRouter } from '@tarojs/taro'
import { useState } from 'react' import { useState } from 'react'
import { observer } from 'mobx-react'
import { ArrowDown } from '@taroify/icons' import { ArrowDown } from '@taroify/icons'
import FooterButtons from './components/FooterButtons' import FooterButtons from './components/FooterButtons'
import { tableStore } from '../../store'
import './index.scss' import './index.scss'
// 使用本地图片资源 // 使用本地图片资源
const phoneIcon = require('../startTable/images/phone.png') const phoneIcon = require('../startTable/images/phone.png')
const locationIcon = require('../../assets/icon/locationIcon.png') const locationIcon = require('../../assets/icon/locationIcon.png')
const CloseTable = () => { const CloseTable = observer(() => {
const router = useRouter() const router = useRouter()
const { type = 'owner' } = router.params as { type?: 'owner' | 'guest' } // owner: 开台人员, guest: 非开台人 const { type = 'owner' } = router.params as { type?: 'owner' | 'guest' } // owner: 开台人员, guest: 非开台人
const [expanded, setExpanded] = useState(false) // 订单详情是否展开 const [expanded, setExpanded] = useState(false) // 订单详情是否展开
const [couponExpanded, setCouponExpanded] = useState(false) // 优惠券详情是否展开 const [couponExpanded, setCouponExpanded] = useState(false) // 优惠券详情是否展开
const [priceExpanded, setPriceExpanded] = 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(() => { 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 = () => {
</View> </View>
{/* 底部按钮区域 */} {/* 底部按钮区域 */}
<FooterButtons type={type} /> <FooterButtons type={type} grpSn={grpSn} operationId={operationId} />
</View> </View>
) )
} })
export default CloseTable export default CloseTable

133
src/pageScan/couponStart/index.tsx

@ -2,8 +2,11 @@
* *
*/ */
import { View, Text, Image, Input } from '@tarojs/components' 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 { useState } from 'react'
import { observer } from 'mobx-react'
import { couponStart, invokeWechatPay } from '../../api/startDevice'
import { userStore, tableStore } from '../../store'
import './index.scss' import './index.scss'
// 使用本地图片资源 // 使用本地图片资源
@ -29,10 +32,15 @@ interface Coupon {
expanded: boolean expanded: boolean
} }
const CouponStart = () => { const CouponStart = observer(() => {
const router = useRouter()
const [couponCode, setCouponCode] = useState('') // 券码 const [couponCode, setCouponCode] = useState('') // 券码
const [selectedCouponType, setSelectedCouponType] = useState<CouponType>(CouponType.THIRD_PARTY) const [selectedCouponType, setSelectedCouponType] = useState<CouponType>(CouponType.THIRD_PARTY)
const [selectedCouponId, setSelectedCouponId] = useState<number>(1) // 选中的券ID 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[]>([ const [coupons, setCoupons] = useState<Coupon[]>([
@ -61,7 +69,12 @@ const CouponStart = () => {
]) ])
useLoad(() => { useLoad(() => {
console.log('团购券开台页面加载') console.log('团购券开台页面加载, 设备编码:', grpSn)
// 保存设备编码到 store
if (grpSn) {
tableStore.setCurrentGrpSn(grpSn)
}
}) })
// 拨打电话 // 拨打电话
@ -109,22 +122,112 @@ const CouponStart = () => {
} }
// 兑换开台 // 兑换开台
const handleExchange = () => { const handleExchange = async () => {
if (!selectedCouponId) { if (loading) return
// 验证券码
if (!couponCode && !selectedCouponId) {
Taro.showToast({ Taro.showToast({
title: '请选择一张券', title: '请输入券码或选择一张券',
icon: 'none', icon: 'none',
duration: 2000 duration: 2000
}) })
return return
} }
console.log('兑换开台,选中券ID:', selectedCouponId) // 获取用户 openId
Taro.showToast({ const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
title: '功能开发中', if (!openId) {
icon: 'none', Taro.showToast({
duration: 2000 title: '请先登录',
}) icon: 'none',
duration: 2000
})
return
}
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: 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 ( return (
@ -303,12 +406,12 @@ const CouponStart = () => {
{/* 底部固定按钮 */} {/* 底部固定按钮 */}
<View className="footer-bar"> <View className="footer-bar">
<View className="exchange-btn" onClick={handleExchange}> <View className={`exchange-btn ${loading ? 'disabled' : ''}`} onClick={handleExchange}>
<Text className="exchange-text"></Text> <Text className="exchange-text">{loading ? '处理中...' : '兑换开台'}</Text>
</View> </View>
</View> </View>
</View> </View>
) )
} })
export default CouponStart export default CouponStart

123
src/pageScan/renewFee/index.tsx

@ -2,21 +2,38 @@
* *
*/ */
import { View, Text, Image } from '@tarojs/components' 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 { useState } from 'react'
import { observer } from 'mobx-react'
import { Stepper } from '@taroify/core' import { Stepper } from '@taroify/core'
import { renewFee, invokeWechatPay, PayType } from '../../api/startDevice'
import { userStore, tableStore } from '../../store'
import './index.scss' import './index.scss'
// 使用本地图片资源 // 使用本地图片资源
const phoneIcon = require('../startTable/images/phone.png') const phoneIcon = require('../startTable/images/phone.png')
const locationIcon = require('../../assets/icon/locationIcon.png') const locationIcon = require('../../assets/icon/locationIcon.png')
const RenewFee = () => { const RenewFee = observer(() => {
const [renewAmount, setRenewAmount] = useState(1000) // 续费金额 const router = useRouter()
const [renewAmount, setRenewAmount] = useState(100) // 续费金额(修改初始值为100)
const [paymentMethod] = useState('微信支付') // 支付方式 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(() => { 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 = () => {
} }
// 立即续费 // 立即续费
const handleConfirmRenew = () => { const handleConfirmRenew = async () => {
console.log('立即续费,金额:', renewAmount) if (loading) return
Taro.showToast({
title: '功能开发中', // 获取用户 openId
icon: 'none', const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
duration: 2000 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: '已取消支付',
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 ( return (
@ -172,12 +267,12 @@ const RenewFee = () => {
<Text className="payment-symbol"></Text> <Text className="payment-symbol"></Text>
<Text className="payment-amount">{renewAmount.toFixed(2)}</Text> <Text className="payment-amount">{renewAmount.toFixed(2)}</Text>
</View> </View>
<View className="confirm-btn" onClick={handleConfirmRenew}> <View className={`confirm-btn ${loading ? 'disabled' : ''}`} onClick={handleConfirmRenew}>
<Text className="confirm-text"></Text> <Text className="confirm-text">{loading ? '处理中...' : '立即续费'}</Text>
</View> </View>
</View> </View>
</View> </View>
) )
} })
export default RenewFee export default RenewFee

117
src/pageScan/selfStart/index.tsx

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

127
src/pageScan/timedStart/index.tsx

@ -2,8 +2,11 @@
* *
*/ */
import { View, Text, Image } from '@tarojs/components' 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 { useState } from 'react'
import { observer } from 'mobx-react'
import { timedStart, invokeWechatPay, PayType } from '../../api/startDevice'
import { userStore, tableStore } from '../../store'
import './index.scss' import './index.scss'
// 使用本地图片资源 // 使用本地图片资源
@ -21,12 +24,22 @@ const durationOptions = [
{ id: 8, label: '8小时', value: 8 }, { id: 8, label: '8小时', value: 8 },
] ]
const TimedStart = () => { const TimedStart = observer(() => {
const router = useRouter()
const [selectedDuration, setSelectedDuration] = useState(1) // 选中的时长 const [selectedDuration, setSelectedDuration] = useState(1) // 选中的时长
const [paymentMethod] = useState('微信支付') // 支付方式 const [paymentMethod] = useState('微信支付') // 支付方式
const [loading, setLoading] = useState(false) // 加载状态
// 从路由参数获取设备信息,如果没有则从 store 获取
const grpSn = tableStore.getGrpSn(router.params.grpSn)
useLoad(() => { useLoad(() => {
console.log('定时开台页面加载') console.log('定时开台页面加载, 设备编码:', grpSn)
// 保存设备编码到 store
if (grpSn) {
tableStore.setCurrentGrpSn(grpSn)
}
}) })
// 拨打电话 // 拨打电话
@ -58,13 +71,101 @@ const TimedStart = () => {
} }
// 立即开台 // 立即开台
const handleConfirmStart = () => { const handleConfirmStart = async () => {
console.log('立即开台,时长:', selectedDuration, '小时') if (loading) return
Taro.showToast({
title: '功能开发中', // 获取用户 openId
icon: 'none', const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
duration: 2000 if (!openId) {
}) Taro.showToast({
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 ( return (
@ -146,12 +247,12 @@ const TimedStart = () => {
<Text className="payment-symbol"></Text> <Text className="payment-symbol"></Text>
<Text className="payment-amount">{calculateAmount()}</Text> <Text className="payment-amount">{calculateAmount()}</Text>
</View> </View>
<View className="confirm-btn" onClick={handleConfirmStart}> <View className={`confirm-btn ${loading ? 'disabled' : ''}`} onClick={handleConfirmStart}>
<Text className="confirm-text"></Text> <Text className="confirm-text">{loading ? '处理中...' : '立即开台'}</Text>
</View> </View>
</View> </View>
</View> </View>
) )
} })
export default TimedStart export default TimedStart

133
src/pageScan/turnOffRestart/index.tsx

@ -2,16 +2,35 @@
* *
*/ */
import { View, Text, Image } from '@tarojs/components' 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' import './index.scss'
// 使用本地图片资源 // 使用本地图片资源
const phoneIcon = require('../startTable/images/phone.png') const phoneIcon = require('../startTable/images/phone.png')
const locationIcon = require('../../assets/icon/locationIcon.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(() => { useLoad(() => {
console.log('关灯重开页面加载') console.log('关灯重开页面加载, 设备编码:', grpSn, '操作记录ID:', operationId)
// 保存到 store
if (grpSn) {
tableStore.setCurrentGrpSn(grpSn)
}
if (operationId) {
tableStore.setCurrentOperationId(operationId)
}
}) })
// 拨打电话 // 拨打电话
@ -24,13 +43,103 @@ const TurnOffRestart = () => {
} }
// 确认结算 // 确认结算
const handleConfirmSettle = () => { const handleConfirmSettle = async () => {
console.log('确认结算') if (loading) return
Taro.showToast({
title: '功能开发中', // 获取用户 openId
icon: 'none', const openId = userStore.userInfo?.wxOpenId || Taro.getStorageSync('openId')
duration: 2000 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: 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 ( return (
@ -109,12 +218,12 @@ const TurnOffRestart = () => {
{/* 底部固定区域 */} {/* 底部固定区域 */}
<View className="footer-bar"> <View className="footer-bar">
<View className="confirm-btn" onClick={handleConfirmSettle}> <View className={`confirm-btn ${loading ? 'disabled' : ''}`} onClick={handleConfirmSettle}>
<Text className="confirm-text"></Text> <Text className="confirm-text">{loading ? '处理中...' : '确认结算'}</Text>
</View> </View>
</View> </View>
</View> </View>
) )
} })
export default TurnOffRestart export default TurnOffRestart

3
src/store/index.ts

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

125
src/store/table.ts

@ -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 @@
/**
*
* /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