Browse Source

简单对接一个开台, 数据暂时写死。

master
DU 5 months ago
parent
commit
0b9a445362
  1. 36
      src/config/index.ts
  2. 118
      src/pageScan/selfStart/index.tsx
  3. 183
      src/services/request.ts
  4. 150
      src/services/tableApi.ts

36
src/config/index.ts

@ -0,0 +1,36 @@
/**
*
* API
*/
// API 基础地址配置
// 直接配置,如需修改请更改此处
const API_BASE_URL = 'https://39.101.75.71:8889'
// API 版本
const API_VERSION = 'v2'
// 完整的 API 前缀
const API_PREFIX = `${API_BASE_URL}/api/${API_VERSION}`
// 是否开启调试模式(可根据需要手动切换)
const DEBUG_MODE = true
export const config = {
// API 基础地址
apiBaseUrl: API_BASE_URL,
// API 版本
apiVersion: API_VERSION,
// 完整 API 前缀
apiPrefix: API_PREFIX,
// 请求超时时间(毫秒)
timeout: 30000,
// 是否开启调试模式
debug: DEBUG_MODE,
}
export default config

118
src/pageScan/selfStart/index.tsx

@ -2,21 +2,27 @@
* *
*/ */
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 { Stepper } from '@taroify/core' import { Stepper } from '@taroify/core'
import { startTable, invokeWechatPay, TableOperationType, PaymentChannel } from '../../services/tableApi'
import './index.scss' import './index.scss'
// 使用本地图片资源 // 使用本地图片资源
const phoneIcon = require('../startTable/images/phone.png') const phoneIcon = require('../startTable/images/phone.png')
const SelfStart = () => { const SelfStart = () => {
const router = useRouter()
const [deposit, setDeposit] = useState(100) // 押金金额 const [deposit, setDeposit] = useState(100) // 押金金额
const [paymentMethod] = useState('微信支付') // 支付方式 const [paymentMethod] = useState('微信支付') // 支付方式
const [storeBalance] = useState(20.00) // 门店余额 const [storeBalance] = useState(20.00) // 门店余额
const [loading, setLoading] = useState(false) // 加载状态
// 从路由参数获取设备信息
const grpSn = router.params.grpSn || '' // 设备编码
useLoad(() => { useLoad(() => {
console.log('自助开台页面加载') console.log('自助开台页面加载, 设备编码:', grpSn)
// 显示提示 Toast // 显示提示 Toast
setTimeout(() => { setTimeout(() => {
@ -69,13 +75,107 @@ const SelfStart = () => {
} }
// 立即开台 // 立即开台
const handleConfirmStart = () => { const handleConfirmStart = async () => {
console.log('立即开台,押金:', deposit) if (loading) return
Taro.showToast({
title: '功能开发中', // 获取用户 openId
icon: 'none', // const openId = 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('立即开台,押金:', 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('开台请求参数:', params)
// 调用开台接口
const result = await startTable(params)
console.log('开台接口返回:', result)
// 判断是否需要支付
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.operationId}`
})
}, 2000)
} catch (payError: any) {
console.log('error:', 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.operationId}`
})
}, 2000)
}
} catch (error: any) {
console.error('开台失败:', error)
// 错误提示由 request 拦截器处理
} finally {
setLoading(false)
}
} }
return ( return (

183
src/services/request.ts

@ -0,0 +1,183 @@
/**
*
* Taro.request HTTP
*/
import Taro from '@tarojs/taro'
import config from '../config'
// 请求配置接口
interface RequestConfig {
url: string
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'TRACE' | 'CONNECT'
data?: any
header?: Record<string, string>
timeout?: number
showLoading?: boolean
loadingText?: string
showError?: boolean
}
// 响应结果接口
interface ResponseResult<T = any> {
message: string
code: number
success: boolean
data: T
}
// 请求拦截器
const requestInterceptor = (options: RequestConfig) => {
// 处理完整 URL
let url = options.url
if (!url.startsWith('http')) {
url = `${config.apiPrefix}${url}`
}
// 默认请求头
const header: Record<string, string> = {
'Content-Type': 'application/json',
...options.header,
}
// 获取 token(如果有)
const token = Taro.getStorageSync('token')
if (token) {
header['Authorization'] = `Bearer ${token}`
}
// 获取 openId(如果有)
const openId = Taro.getStorageSync('openId')
if (openId) {
header['X-OpenId'] = openId
}
return {
...options,
url,
header,
}
}
// 响应拦截器
const responseInterceptor = <T>(response: Taro.request.SuccessCallbackResult<ResponseResult<T>>): ResponseResult<T> => {
const { statusCode, data } = response
if (statusCode >= 200 && statusCode < 300) {
// 请求成功
if (data.success) {
return data
} else {
// 业务错误
throw {
code: data.code,
message: data.message || '请求失败',
data: data.data,
}
}
} else if (statusCode === 401) {
// 未授权,清除登录信息
Taro.removeStorageSync('token')
Taro.removeStorageSync('openId')
throw {
code: 401,
message: '登录已过期,请重新登录',
}
} else if (statusCode === 400) {
// 参数错误
throw {
code: data.code || 400,
message: data.message || '请求参数错误',
data: data.data,
}
} else if (statusCode === 500) {
// 服务器错误
throw {
code: data.code || 500,
message: data.message || '服务器内部错误',
data: data.data,
}
} else {
throw {
code: statusCode,
message: `请求失败: ${statusCode}`,
}
}
}
// 封装请求方法
const request = async <T = any>(options: RequestConfig): Promise<ResponseResult<T>> => {
const {
showLoading = false,
loadingText = '加载中...',
showError = true,
timeout = config.timeout,
...restOptions
} = options
// 显示加载提示
if (showLoading) {
Taro.showLoading({ title: loadingText, mask: true })
}
try {
// 请求拦截
const finalOptions = requestInterceptor(restOptions)
if (config.debug) {
console.log('[Request]', finalOptions.method || 'GET', finalOptions.url, finalOptions.data)
}
// 发起请求
const response = await Taro.request<ResponseResult<T>>({
...finalOptions,
timeout,
})
if (config.debug) {
console.log('[Response]', response.statusCode, response.data)
}
// 响应拦截
return responseInterceptor<T>(response)
} catch (error: any) {
if (config.debug) {
console.error('[Request Error]', error)
}
// 显示错误提示
if (showError) {
const errorMessage = error.message || error.errMsg || '网络请求失败'
Taro.showToast({
title: errorMessage,
icon: 'none',
duration: 2000,
})
}
throw error
} finally {
// 隐藏加载提示
if (showLoading) {
Taro.hideLoading()
}
}
}
// 快捷方法
export const get = <T = any>(url: string, data?: any, options?: Partial<RequestConfig>) => {
return request<T>({ url, method: 'GET', data, ...options })
}
export const post = <T = any>(url: string, data?: any, options?: Partial<RequestConfig>) => {
return request<T>({ url, method: 'POST', data, ...options })
}
export const put = <T = any>(url: string, data?: any, options?: Partial<RequestConfig>) => {
return request<T>({ url, method: 'PUT', data, ...options })
}
export const del = <T = any>(url: string, data?: any, options?: Partial<RequestConfig>) => {
return request<T>({ url, method: 'DELETE', data, ...options })
}
export default request

150
src/services/tableApi.ts

@ -0,0 +1,150 @@
/**
* API
*
*/
import { post } from './request'
/**
*
*/
export enum TableOperationType {
/** 押金开台 */
SelfServiceDeposit = 1,
/** 余额开台 */
SelfServiceBalance = 2,
/** 定时开台 */
Timed = 3,
/** 信用开台 */
Credit = 4,
/** 团购券开台 */
GroupBuy = 5,
/** 立即关台 */
ImmediateClose = 6,
/** 续费 */
Renew = 7,
/** 关灯重开 */
ReopenAfterClose = 8,
}
/**
*
*/
export enum PaymentChannel {
/** 微信支付 */
WechatPay = 1,
/** 支付宝 */
Alipay = 2,
/** 银联支付 */
UnionPay = 3,
/** 余额支付 */
Balance = 4,
/** 银行卡支付 */
BankCard = 5,
}
/**
*
*/
export interface StartTableRequest {
/** 设备编码 */
grpSn: string
/** 用户 OpenId */
openId: string
/** 开台操作类型 */
ctrlType: TableOperationType
/** 消费金额 */
amount?: number
/** 支付渠道 */
payType?: PaymentChannel
/** 开台时长(小时),定时开台使用 */
durationHours?: number
/** 团购券码,团购券开台使用 */
couponCode?: string
}
/**
*
*/
export interface StartTableResponse {
/** 机构编码 */
instSn: string
/** 店铺编码 */
bpSn: string
/** 球台编码 */
grpSn: string
/** 开台设备id */
operationId: number
/** 开始时间 */
startTime: string
/** 结束时间 */
expectedEndTime: string
/** 开台操作详情id */
detailIds: number[]
/** 控制id */
controlMessageId: number
/** 商户订单号 */
outTradeNo: string
/** 支付参数(用于调起微信支付) */
payParameters: WechatPayParameters | null
/** 支付URL(用于扫码支付) */
payUrl: string
}
/**
*
*/
export interface WechatPayParameters {
/** 时间戳 */
timeStamp: string
/** 随机字符串 */
nonceStr: string
/** 统一下单接口返回的 prepay_id */
package: string
/** 签名方式 */
signType: string
/** 签名 */
paySign: string
}
/**
* API
* @param data
* @returns
*/
export const startTable = (data: StartTableRequest) => {
return post<StartTableResponse>('/Tables/start', data, {
showLoading: true,
loadingText: '开台中...',
})
}
/**
*
* @param payParams
* @returns Promise
*/
export const invokeWechatPay = (payParams: WechatPayParameters): Promise<void> => {
return new Promise((resolve, reject) => {
// 使用 wx.requestPayment 调起微信支付
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)
},
})
})
}
export default {
startTable,
invokeWechatPay,
TableOperationType,
PaymentChannel,
}
Loading…
Cancel
Save