diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..e754ebc --- /dev/null +++ b/src/App.vue @@ -0,0 +1,64 @@ + + + + + + diff --git a/src/config/index.js b/src/config/index.js new file mode 100644 index 0000000..1770408 --- /dev/null +++ b/src/config/index.js @@ -0,0 +1,24 @@ +/** + * 配置文件 + */ + +export default { + // API基础URL + apiBaseUrl: import.meta.env.VITE_API_BASE_URL || '/api', + + // PingPong SDK模式 + pingpongMode: import.meta.env.VITE_PINGPONG_MODE || 'sandbox', + + // PingPong SDK URL + pingpongSdkUrl: 'https://pay-cdn.pingpongx.com/production/static/sdk/1.2.0/ppPay.min.js', + + // 请求超时时间(毫秒) + requestTimeout: 30000, + + // 分页配置 + pagination: { + pageSize: 10, + pageSizes: [10, 20, 50, 100] + } +} + diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..d5264c4 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,36 @@ +import { createRouter, createWebHistory } from 'vue-router' +import CreateOrder from '../views/CreateOrder.vue' +import Checkout from '../views/Checkout.vue' +import PaymentResult from '../views/PaymentResult.vue' +import OrderQuery from '../views/OrderQuery.vue' + +const routes = [ + { + path: '/', + name: 'CreateOrder', + component: CreateOrder + }, + { + path: '/checkout', + name: 'Checkout', + component: Checkout + }, + { + path: '/result', + name: 'PaymentResult', + component: PaymentResult + }, + { + path: '/query', + name: 'OrderQuery', + component: OrderQuery + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +export default router + diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..07fd1ec --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,42 @@ +import { reactive } from 'vue' + +/** + * 简单的状态管理 + * 如果需要更复杂的状态管理,可以使用 Pinia + */ + +const state = reactive({ + // 用户信息 + user: null, + // 当前订单 + currentOrder: null, + // 加载状态 + loading: false +}) + +export default { + state, + + // 设置用户信息 + setUser(user) { + state.user = user + }, + + // 设置当前订单 + setCurrentOrder(order) { + state.currentOrder = order + }, + + // 设置加载状态 + setLoading(loading) { + state.loading = loading + }, + + // 清除状态 + clear() { + state.user = null + state.currentOrder = null + state.loading = false + } +} + diff --git a/src/utils/constants.js b/src/utils/constants.js new file mode 100644 index 0000000..d877a9b --- /dev/null +++ b/src/utils/constants.js @@ -0,0 +1,75 @@ +/** + * 常量定义 + */ + +// 订单状态 +export const ORDER_STATUS = { + PENDING: 'PENDING', + SUCCESS: 'SUCCESS', + FAILED: 'FAILED', + REVIEW: 'REVIEW', + CANCELLED: 'CANCELLED' +} + +// 订单状态文本 +export const ORDER_STATUS_TEXT = { + [ORDER_STATUS.PENDING]: '待支付', + [ORDER_STATUS.SUCCESS]: '支付成功', + [ORDER_STATUS.FAILED]: '支付失败', + [ORDER_STATUS.REVIEW]: '审核中', + [ORDER_STATUS.CANCELLED]: '已取消' +} + +// 支付类型 +export const PAYMENT_TYPE = { + SALE: 'SALE', + AUTH: 'AUTH' +} + +// 支付类型文本 +export const PAYMENT_TYPE_TEXT = { + [PAYMENT_TYPE.SALE]: '直接付款', + [PAYMENT_TYPE.AUTH]: '预授权' +} + +// 币种 +export const CURRENCY = { + USD: 'USD', + EUR: 'EUR', + GBP: 'GBP', + CNY: 'CNY', + JPY: 'JPY' +} + +// 币种文本 +export const CURRENCY_TEXT = { + [CURRENCY.USD]: '美元', + [CURRENCY.EUR]: '欧元', + [CURRENCY.GBP]: '英镑', + [CURRENCY.CNY]: '人民币', + [CURRENCY.JPY]: '日元' +} + +// 响应码 +export const RESPONSE_CODE = { + SUCCESS: '0000', + FAIL: '9999', + PARAM_ERROR: '4000', + VALIDATION_ERROR: '4001', + ORDER_NOT_FOUND: '1001', + ORDER_EXISTS: '1002' +} + +// API基础路径 +export const API_BASE_URL = '/api' + +// PingPong SDK URL +export const PINGPONG_SDK_URL = 'https://pay-cdn.pingpongx.com/production/static/sdk/1.2.0/ppPay.min.js' + +// PingPong SDK模式 +export const PINGPONG_MODE = { + SANDBOX: 'sandbox', + TEST: 'test', + BUILD: 'build' +} + diff --git a/src/utils/helpers.js b/src/utils/helpers.js new file mode 100644 index 0000000..6d0c49d --- /dev/null +++ b/src/utils/helpers.js @@ -0,0 +1,123 @@ +/** + * 工具函数 + */ + +/** + * 格式化金额 + */ +export function formatAmount(amount, currency = 'USD') { + if (!amount) return '0.00' + return parseFloat(amount).toFixed(2) +} + +/** + * 格式化日期时间 + */ +export function formatDateTime(dateTime, format = 'YYYY-MM-DD HH:mm:ss') { + if (!dateTime) return '' + + const date = new Date(dateTime) + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + const seconds = String(date.getSeconds()).padStart(2, '0') + + return format + .replace('YYYY', year) + .replace('MM', month) + .replace('DD', day) + .replace('HH', hours) + .replace('mm', minutes) + .replace('ss', seconds) +} + +/** + * 生成订单号 + */ +export function generateOrderId() { + const timestamp = Date.now() + const random = Math.floor(Math.random() * 10000) + return `MTN${timestamp}${random.toString().padStart(4, '0')}` +} + +/** + * 获取订单状态标签类型 + */ +export function getStatusTagType(status) { + if (!status) return 'info' + const statusUpper = status.toUpperCase() + if (statusUpper === 'SUCCESS' || statusUpper === 'SUCCESSFUL') { + return 'success' + } else if (statusUpper === 'FAILED' || statusUpper === 'FAILURE') { + return 'danger' + } else if (statusUpper === 'REVIEW') { + return 'warning' + } + return 'info' +} + +/** + * 获取订单状态文本 + */ +export function getStatusText(status) { + if (!status) return '未知' + const statusMap = { + 'PENDING': '待支付', + 'SUCCESS': '支付成功', + 'SUCCESSFUL': '支付成功', + 'FAILED': '支付失败', + 'FAILURE': '支付失败', + 'REVIEW': '审核中', + 'CANCELLED': '已取消', + 'CANCEL': '已取消' + } + return statusMap[status.toUpperCase()] || status +} + +/** + * 验证邮箱 + */ +export function validateEmail(email) { + const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ + return re.test(email) +} + +/** + * 验证手机号(简单验证) + */ +export function validatePhone(phone) { + const re = /^1[3-9]\d{9}$/ + return re.test(phone) +} + +/** + * 防抖函数 + */ +export function debounce(func, wait) { + let timeout + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout) + func(...args) + } + clearTimeout(timeout) + timeout = setTimeout(later, wait) + } +} + +/** + * 节流函数 + */ +export function throttle(func, limit) { + let inThrottle + return function(...args) { + if (!inThrottle) { + func.apply(this, args) + inThrottle = true + setTimeout(() => inThrottle = false, limit) + } + } +} + diff --git a/src/views/Checkout.vue b/src/views/Checkout.vue new file mode 100644 index 0000000..babf46c --- /dev/null +++ b/src/views/Checkout.vue @@ -0,0 +1,139 @@ + + + + + +