ishop/public/js/main.js
2025-08-10 12:20:27 +08:00

369 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 全局变量
let currentProduct = null;
let currentOrder = null;
// DOM 元素
const productPrice = document.getElementById('product-price');
const quantityInput = document.getElementById('quantity');
const totalPriceElement = document.getElementById('total-price');
const orderForm = document.getElementById('orderForm');
const orderModal = document.getElementById('orderModal');
const paymentStatus = document.getElementById('paymentStatus');
// 初始化
document.addEventListener('DOMContentLoaded', function() {
loadProductData();
setupEventListeners();
});
// 加载产品数据
async function loadProductData() {
try {
const response = await fetch('/api/products');
const products = await response.json();
// 使用第一个产品作为展示产品
const productId = Object.keys(products)[0];
currentProduct = { id: productId, ...products[productId] };
// 更新界面
document.getElementById('product-name').textContent = currentProduct.name;
document.getElementById('product-description').textContent = currentProduct.description;
document.getElementById('product-price').textContent = currentProduct.price.toFixed(2);
updateTotalPrice();
} catch (error) {
console.error('加载产品数据失败:', error);
alert('加载产品信息失败,请刷新页面重试');
}
}
// 设置事件监听器
function setupEventListeners() {
// 数量控制
document.getElementById('decrease-qty').addEventListener('click', () => {
const current = parseInt(quantityInput.value) || 1;
if (current > 1) {
quantityInput.value = current - 1;
updateTotalPrice();
}
});
document.getElementById('increase-qty').addEventListener('click', () => {
const current = parseInt(quantityInput.value) || 1;
if (current < 999) {
quantityInput.value = current + 1;
updateTotalPrice();
}
});
// 数量输入变化
quantityInput.addEventListener('input', updateTotalPrice);
quantityInput.addEventListener('change', validateQuantity);
// 表单提交
orderForm.addEventListener('submit', handleOrderSubmit);
// 模态框控制
document.getElementById('closeModal').addEventListener('click', closeOrderModal);
document.getElementById('pay-now-btn').addEventListener('click', handlePayNow);
// 支付状态检查
document.getElementById('check-status-btn').addEventListener('click', checkPaymentStatus);
// 点击模态框外部关闭
orderModal.addEventListener('click', (e) => {
if (e.target === orderModal) {
closeOrderModal();
}
});
}
// 更新总价
function updateTotalPrice() {
if (!currentProduct) return;
const quantity = parseInt(quantityInput.value) || 1;
const total = (currentProduct.price * quantity).toFixed(2);
totalPriceElement.textContent = `$${total}`;
}
// 验证数量
function validateQuantity() {
const value = parseInt(quantityInput.value);
if (isNaN(value) || value < 1) {
quantityInput.value = 1;
} else if (value > 999) {
quantityInput.value = 999;
}
updateTotalPrice();
}
// 处理订单提交
async function handleOrderSubmit(e) {
e.preventDefault();
if (!currentProduct) {
alert('产品信息加载中,请稍后重试');
return;
}
// 获取表单数据
const formData = new FormData(orderForm);
const orderData = {
product_id: currentProduct.id,
quantity: parseInt(formData.get('quantity')),
customer_name: formData.get('customer_name').trim(),
customer_email: formData.get('customer_email').trim(),
customer_phone: formData.get('customer_phone').trim(),
shipping_address: formData.get('shipping_address').trim()
};
// 验证必填字段
if (!orderData.customer_name || !orderData.shipping_address) {
alert('请填写姓名和收货地址');
return;
}
// 禁用提交按钮
const submitBtn = document.getElementById('submit-order');
const btnText = submitBtn.querySelector('.btn-text');
const btnLoading = submitBtn.querySelector('.btn-loading');
submitBtn.disabled = true;
btnText.style.display = 'none';
btnLoading.style.display = 'inline';
try {
// 创建订单
const response = await fetch('/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(orderData)
});
const result = await response.json();
if (result.success) {
currentOrder = {
...orderData,
order_id: result.order_id,
total_amount: result.total_amount
};
showOrderModal();
} else {
throw new Error(result.error || '订单创建失败');
}
} catch (error) {
console.error('创建订单失败:', error);
alert('创建订单失败: ' + error.message);
} finally {
// 恢复提交按钮
submitBtn.disabled = false;
btnText.style.display = 'inline';
btnLoading.style.display = 'none';
}
}
// 显示订单确认模态框
function showOrderModal() {
if (!currentOrder) return;
// 填充订单信息
document.getElementById('modal-order-id').textContent = currentOrder.order_id;
document.getElementById('modal-product-name').textContent = currentProduct.name;
document.getElementById('modal-quantity').textContent = currentOrder.quantity;
document.getElementById('modal-total-price').textContent = `$${currentOrder.total_amount.toFixed(2)} USDT`;
document.getElementById('modal-address').textContent = currentOrder.shipping_address;
orderModal.style.display = 'flex';
}
// 关闭订单模态框
function closeOrderModal() {
orderModal.style.display = 'none';
}
// 处理支付
async function handlePayNow() {
if (!currentOrder) return;
try {
// 创建支付
const response = await fetch('/api/payment/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
order_id: currentOrder.order_id
})
});
const result = await response.json();
if (result.success) {
// 检查是否为手动支付模式
if (result.manual_mode) {
// 关闭模态框,跳转到手动支付页面
closeOrderModal();
window.location.href = result.payment_url;
} else {
// 关闭模态框直接跳转到UPay支付页面
closeOrderModal();
// 显示跳转提示
const jumpTip = document.createElement('div');
jumpTip.innerHTML = `
<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 9999; display: flex; align-items: center; justify-content: center;">
<div style="background: white; padding: 30px; border-radius: 10px; text-align: center;">
<h3>正在跳转到支付页面...</h3>
<p>请在新页面完成USDT支付</p>
<div style="margin: 20px 0;">
<div style="width: 40px; height: 40px; border: 4px solid #f3f3f3; border-top: 4px solid #667eea; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto;"></div>
</div>
<button onclick="this.parentElement.parentElement.remove(); showPaymentStatus();" style="background: #667eea; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer;">我已完成支付</button>
</div>
</div>
`;
document.body.appendChild(jumpTip);
// 3秒后跳转到UPay支付页面
setTimeout(() => {
window.location.href = result.payment_url;
}, 3000);
}
} else {
throw new Error(result.error || '创建支付失败');
}
} catch (error) {
console.error('创建支付失败:', error);
alert('创建支付失败: ' + error.message);
}
}
// 显示支付状态页面
function showPaymentStatus() {
if (!currentOrder) return;
document.getElementById('status-order-id').textContent = currentOrder.order_id;
document.getElementById('status-amount').textContent = currentOrder.total_amount.toFixed(2);
paymentStatus.style.display = 'flex';
// 开始监听支付状态
startPaymentStatusCheck();
}
// 开始支付状态检查
function startPaymentStatusCheck() {
// 每10秒检查一次支付状态
const checkInterval = setInterval(async () => {
const status = await checkPaymentStatus();
if (status === 'finished' || status === 'failed') {
clearInterval(checkInterval);
}
}, 10000);
}
// 检查支付状态
async function checkPaymentStatus() {
if (!currentOrder) return;
try {
const response = await fetch(`/api/orders/${currentOrder.order_id}`);
const order = await response.json();
const statusIcon = document.getElementById('status-icon');
const statusTitle = document.getElementById('status-title');
const statusMessage = document.getElementById('status-message');
switch (order.payment_status) {
case 'finished':
statusIcon.innerHTML = '<div class="success-icon">✓</div>';
statusTitle.textContent = '支付成功!';
statusMessage.textContent = '您的订单已确认,我们将尽快处理并发货。';
break;
case 'failed':
case 'expired':
statusIcon.innerHTML = '<div class="error-icon">✗</div>';
statusTitle.textContent = '支付失败';
statusMessage.textContent = '支付未完成或已过期,请重新下单。';
break;
case 'confirming':
statusTitle.textContent = '支付确认中...';
statusMessage.textContent = '我们已收到您的支付,正在等待区块链确认。';
break;
default:
statusTitle.textContent = '等待支付...';
statusMessage.textContent = '请完成USDT支付我们正在等待您的交易。';
}
return order.payment_status;
} catch (error) {
console.error('检查支付状态失败:', error);
return null;
}
}
// 工具函数:验证邮箱
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// 工具函数:验证电话号码
function validatePhone(phone) {
const phoneRegex = /^[\d\s\-\+\(\)]+$/;
return phoneRegex.test(phone);
}
// 管理员登录相关函数
function showAdminLogin() {
document.getElementById('adminLoginModal').style.display = 'flex';
document.getElementById('adminPassword').focus();
}
function closeAdminLogin() {
document.getElementById('adminLoginModal').style.display = 'none';
document.getElementById('adminPassword').value = '';
document.getElementById('loginError').style.display = 'none';
}
function verifyAdminPassword() {
const password = document.getElementById('adminPassword').value;
const errorDiv = document.getElementById('loginError');
if (password === '223388') {
// 密码正确,跳转到管理页面
window.location.href = '/admin.html';
} else {
// 密码错误
errorDiv.textContent = '密码错误,请重新输入';
errorDiv.style.display = 'block';
document.getElementById('adminPassword').value = '';
document.getElementById('adminPassword').focus();
}
}
// 键盘事件监听
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeAdminLogin();
}
});
// 管理员登录密码框回车事件
document.addEventListener('DOMContentLoaded', function() {
const adminPasswordInput = document.getElementById('adminPassword');
if (adminPasswordInput) {
adminPasswordInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
verifyAdminPassword();
}
});
}
});