diff --git a/src/App.vue b/src/App.vue
index 0b3bbf7..14fca00 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -9,14 +9,22 @@
@@ -56,6 +64,7 @@ const activeIndex = computed(() => route.path)
color: white;
line-height: 60px;
padding: 0 20px;
+ overflow: visible;
}
.header-content {
@@ -64,6 +73,58 @@ const activeIndex = computed(() => route.path)
align-items: center;
}
+/* 导航菜单样式 - 直接显示,不隐藏 */
+.nav-menu {
+ display: flex;
+ gap: 0;
+ align-items: center;
+}
+
+.nav-item {
+ display: inline-block;
+ padding: 0 20px;
+ line-height: 60px;
+ color: white;
+ text-decoration: none;
+ font-size: 14px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ white-space: nowrap;
+ border-bottom: 2px solid transparent;
+}
+
+.nav-item:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+}
+
+.nav-item.active {
+ background-color: rgba(255, 255, 255, 0.2);
+ border-bottom: 2px solid white;
+}
+
+/* 移动端适配 */
+@media (max-width: 768px) {
+ .header-content {
+ flex-wrap: wrap;
+ }
+
+ .header-content h1 {
+ font-size: 16px;
+ margin-bottom: 0;
+ }
+
+ .nav-menu {
+ width: 100%;
+ justify-content: center;
+ margin-top: 0;
+ }
+
+ .nav-item {
+ padding: 0 15px;
+ font-size: 14px;
+ }
+}
+
.header-content h1 {
font-size: 20px;
margin: 0;
diff --git a/src/api/request.js b/src/api/request.js
index d30018d..e2f3247 100644
--- a/src/api/request.js
+++ b/src/api/request.js
@@ -17,6 +17,17 @@ request.interceptors.request.use(
if (config.data instanceof FormData) {
delete config.headers['Content-Type']
}
+
+ // 只有访问ERP接口时才添加Token
+ // 客户接口(商品、订单等)不需要Token
+ const isErpApi = config.url?.includes('/erp/')
+ if (isErpApi) {
+ const token = localStorage.getItem('token')
+ if (token) {
+ config.headers['Authorization'] = `Bearer ${token}`
+ }
+ }
+
return config
},
error => {
@@ -48,7 +59,32 @@ request.interceptors.response.use(
let message = '请求失败'
if (error.response) {
const data = error.response.data
- message = data?.message || `请求失败: ${error.response.status}`
+ const status = error.response.status
+
+ // 处理401未授权错误(Token过期或无效)
+ // 只有访问ERP接口时才需要处理认证错误
+ if (status === 401 || data?.code === '4002' || data?.code === '7004') {
+ // 清除Token和用户信息
+ localStorage.removeItem('token')
+ localStorage.removeItem('userInfo')
+
+ // 只有访问ERP相关接口或管理页面时才跳转到登录页
+ const isErpApi = error.config?.url?.includes('/erp/')
+ const isManagePage = window.location.pathname.startsWith('/manage')
+
+ if (isErpApi || isManagePage) {
+ if (window.location.pathname !== '/login' && window.location.pathname !== '/register') {
+ ElMessage.error('登录已过期,请重新登录')
+ setTimeout(() => {
+ window.location.href = '/login'
+ }, 1000)
+ }
+ }
+
+ message = data?.message || '登录已过期,请重新登录'
+ } else {
+ message = data?.message || `请求失败: ${status}`
+ }
} else if (error.request) {
message = '网络错误,请检查网络连接'
} else {
diff --git a/src/i18n/locales.js b/src/i18n/locales.js
index 76f9fc2..5cda75c 100644
--- a/src/i18n/locales.js
+++ b/src/i18n/locales.js
@@ -165,6 +165,46 @@ const zh = {
currencySGD: '新加坡元',
currencyHKD: '港币',
currencyPHP: '菲律宾比索'
+ },
+ // 用户相关
+ user: {
+ login: '登录',
+ register: '注册',
+ logout: '退出登录',
+ username: '账号',
+ password: '密码',
+ nickName: '用户名称',
+ phone: '手机号',
+ email: '邮箱',
+ storeCode: '店铺号',
+ loginTitle: '用户登录',
+ registerTitle: '用户注册',
+ loginSuccess: '登录成功',
+ registerSuccess: '注册成功',
+ logoutSuccess: '退出登录成功',
+ usernameRequired: '请输入账号',
+ passwordRequired: '请输入密码',
+ storeCodeRequired: '请输入店铺号',
+ usernamePlaceholder: '请输入账号(3-50个字符)',
+ passwordPlaceholder: '请输入密码(6-20个字符)',
+ nickNamePlaceholder: '请输入用户名称(可选)',
+ phonePlaceholder: '请输入手机号(可选)',
+ emailPlaceholder: '请输入邮箱(可选)',
+ storeCodePlaceholder: '请输入店铺号',
+ usernameInvalid: '账号只能包含字母、数字和下划线',
+ passwordInvalid: '密码长度必须在6-20个字符之间',
+ phoneInvalid: '手机号格式不正确',
+ emailInvalid: '邮箱格式不正确',
+ noAccount: '还没有账号?',
+ hasAccount: '已有账号?',
+ goRegister: '立即注册',
+ goLogin: '立即登录',
+ rememberMe: '记住我',
+ forgotPassword: '忘记密码?',
+ userInfo: '用户信息',
+ welcome: '欢迎',
+ lastLoginTime: '最后登录时间',
+ lastLoginIp: '最后登录IP'
}
}
@@ -327,6 +367,46 @@ const en = {
currencySGD: 'Singapore Dollar',
currencyHKD: 'Hong Kong Dollar',
currencyPHP: 'Philippine Peso'
+ },
+ // 用户相关
+ user: {
+ login: 'Login',
+ register: 'Register',
+ logout: 'Logout',
+ username: 'Username',
+ password: 'Password',
+ nickName: 'Nick Name',
+ phone: 'Phone',
+ email: 'Email',
+ storeCode: 'Store Code',
+ loginTitle: 'User Login',
+ registerTitle: 'User Register',
+ loginSuccess: 'Login successful',
+ registerSuccess: 'Registration successful',
+ logoutSuccess: 'Logout successful',
+ usernameRequired: 'Please enter username',
+ passwordRequired: 'Please enter password',
+ storeCodeRequired: 'Please enter store code',
+ usernamePlaceholder: 'Please enter username (3-50 characters)',
+ passwordPlaceholder: 'Please enter password (6-20 characters)',
+ nickNamePlaceholder: 'Please enter nick name (optional)',
+ phonePlaceholder: 'Please enter phone number (optional)',
+ emailPlaceholder: 'Please enter email (optional)',
+ storeCodePlaceholder: 'Please enter store code',
+ usernameInvalid: 'Username can only contain letters, numbers and underscores',
+ passwordInvalid: 'Password length must be between 6-20 characters',
+ phoneInvalid: 'Invalid phone number format',
+ emailInvalid: 'Invalid email format',
+ noAccount: 'No account yet?',
+ hasAccount: 'Already have an account?',
+ goRegister: 'Register now',
+ goLogin: 'Login now',
+ rememberMe: 'Remember me',
+ forgotPassword: 'Forgot password?',
+ userInfo: 'User Information',
+ welcome: 'Welcome',
+ lastLoginTime: 'Last Login Time',
+ lastLoginIp: 'Last Login IP'
}
}
@@ -486,6 +566,46 @@ const may = {
currencySGD: 'Dolar Singapura',
currencyHKD: 'Dolar Hong Kong',
currencyPHP: 'Peso Filipina'
+ },
+ // 用户相关
+ user: {
+ login: 'Log Masuk',
+ register: 'Daftar',
+ logout: 'Log Keluar',
+ username: 'Nama Pengguna',
+ password: 'Kata Laluan',
+ nickName: 'Nama Panggilan',
+ phone: 'Telefon',
+ email: 'E-mel',
+ storeCode: 'Kod Kedai',
+ loginTitle: 'Log Masuk Pengguna',
+ registerTitle: 'Pendaftaran Pengguna',
+ loginSuccess: 'Log masuk berjaya',
+ registerSuccess: 'Pendaftaran berjaya',
+ logoutSuccess: 'Log keluar berjaya',
+ usernameRequired: 'Sila masukkan nama pengguna',
+ passwordRequired: 'Sila masukkan kata laluan',
+ storeCodeRequired: 'Sila masukkan kod kedai',
+ usernamePlaceholder: 'Sila masukkan nama pengguna (3-50 aksara)',
+ passwordPlaceholder: 'Sila masukkan kata laluan (6-20 aksara)',
+ nickNamePlaceholder: 'Sila masukkan nama panggilan (pilihan)',
+ phonePlaceholder: 'Sila masukkan nombor telefon (pilihan)',
+ emailPlaceholder: 'Sila masukkan e-mel (pilihan)',
+ storeCodePlaceholder: 'Sila masukkan kod kedai',
+ usernameInvalid: 'Nama pengguna hanya boleh mengandungi huruf, nombor dan garis bawah',
+ passwordInvalid: 'Panjang kata laluan mestilah antara 6-20 aksara',
+ phoneInvalid: 'Format nombor telefon tidak sah',
+ emailInvalid: 'Format e-mel tidak sah',
+ noAccount: 'Belum ada akaun?',
+ hasAccount: 'Sudah ada akaun?',
+ goRegister: 'Daftar sekarang',
+ goLogin: 'Log masuk sekarang',
+ rememberMe: 'Ingat saya',
+ forgotPassword: 'Lupa kata laluan?',
+ userInfo: 'Maklumat Pengguna',
+ welcome: 'Selamat datang',
+ lastLoginTime: 'Masa Log Masuk Terakhir',
+ lastLoginIp: 'IP Log Masuk Terakhir'
}
}
@@ -592,6 +712,46 @@ const fil = {
currencySGD: 'Singapore Dollar',
currencyHKD: 'Hong Kong Dollar',
currencyPHP: 'Philippine Peso'
+ },
+ // 用户相关
+ user: {
+ login: 'Mag-login',
+ register: 'Magrehistro',
+ logout: 'Mag-logout',
+ username: 'Username',
+ password: 'Password',
+ nickName: 'Palayaw',
+ phone: 'Telepono',
+ email: 'Email',
+ storeCode: 'Store Code',
+ loginTitle: 'User Login',
+ registerTitle: 'User Register',
+ loginSuccess: 'Matagumpay na pag-login',
+ registerSuccess: 'Matagumpay na pagrehistro',
+ logoutSuccess: 'Matagumpay na pag-logout',
+ usernameRequired: 'Mangyaring maglagay ng username',
+ passwordRequired: 'Mangyaring maglagay ng password',
+ storeCodeRequired: 'Mangyaring maglagay ng store code',
+ usernamePlaceholder: 'Mangyaring maglagay ng username (3-50 characters)',
+ passwordPlaceholder: 'Mangyaring maglagay ng password (6-20 characters)',
+ nickNamePlaceholder: 'Mangyaring maglagay ng palayaw (opsyonal)',
+ phonePlaceholder: 'Mangyaring maglagay ng numero ng telepono (opsyonal)',
+ emailPlaceholder: 'Mangyaring maglagay ng email (opsyonal)',
+ storeCodePlaceholder: 'Mangyaring maglagay ng store code',
+ usernameInvalid: 'Ang username ay maaari lamang maglaman ng mga titik, numero at underscore',
+ passwordInvalid: 'Ang haba ng password ay dapat nasa pagitan ng 6-20 characters',
+ phoneInvalid: 'Hindi wasto ang format ng numero ng telepono',
+ emailInvalid: 'Hindi wasto ang format ng email',
+ noAccount: 'Wala pang account?',
+ hasAccount: 'Mayroon nang account?',
+ goRegister: 'Magrehistro ngayon',
+ goLogin: 'Mag-login ngayon',
+ rememberMe: 'Tandaan ako',
+ forgotPassword: 'Nakalimutan ang password?',
+ userInfo: 'Impormasyon ng User',
+ welcome: 'Maligayang pagdating',
+ lastLoginTime: 'Huling Oras ng Login',
+ lastLoginIp: 'Huling IP ng Login'
}
}
@@ -750,6 +910,46 @@ const th = {
currencySGD: 'ดอลลาร์สิงคโปร์',
currencyHKD: 'ดอลลาร์ฮ่องกง',
currencyPHP: 'เปโซฟิลิปปินส์'
+ },
+ // 用户相关
+ user: {
+ login: 'เข้าสู่ระบบ',
+ register: 'ลงทะเบียน',
+ logout: 'ออกจากระบบ',
+ username: 'ชื่อผู้ใช้',
+ password: 'รหัสผ่าน',
+ nickName: 'ชื่อเล่น',
+ phone: 'เบอร์โทรศัพท์',
+ email: 'อีเมล',
+ storeCode: 'รหัสร้านค้า',
+ loginTitle: 'เข้าสู่ระบบผู้ใช้',
+ registerTitle: 'ลงทะเบียนผู้ใช้',
+ loginSuccess: 'เข้าสู่ระบบสำเร็จ',
+ registerSuccess: 'ลงทะเบียนสำเร็จ',
+ logoutSuccess: 'ออกจากระบบสำเร็จ',
+ usernameRequired: 'กรุณากรอกชื่อผู้ใช้',
+ passwordRequired: 'กรุณากรอกรหัสผ่าน',
+ storeCodeRequired: 'กรุณากรอกรหัสร้านค้า',
+ usernamePlaceholder: 'กรุณากรอกชื่อผู้ใช้ (3-50 ตัวอักษร)',
+ passwordPlaceholder: 'กรุณากรอกรหัสผ่าน (6-20 ตัวอักษร)',
+ nickNamePlaceholder: 'กรุณากรอกชื่อเล่น (ไม่บังคับ)',
+ phonePlaceholder: 'กรุณากรอกเบอร์โทรศัพท์ (ไม่บังคับ)',
+ emailPlaceholder: 'กรุณากรอกอีเมล (ไม่บังคับ)',
+ storeCodePlaceholder: 'กรุณากรอกรหัสร้านค้า',
+ usernameInvalid: 'ชื่อผู้ใช้สามารถมีได้เฉพาะตัวอักษร ตัวเลข และขีดล่าง',
+ passwordInvalid: 'ความยาวรหัสผ่านต้องอยู่ระหว่าง 6-20 ตัวอักษร',
+ phoneInvalid: 'รูปแบบเบอร์โทรศัพท์ไม่ถูกต้อง',
+ emailInvalid: 'รูปแบบอีเมลไม่ถูกต้อง',
+ noAccount: 'ยังไม่มีบัญชี?',
+ hasAccount: 'มีบัญชีแล้ว?',
+ goRegister: 'ลงทะเบียนตอนนี้',
+ goLogin: 'เข้าสู่ระบบตอนนี้',
+ rememberMe: 'จำฉันไว้',
+ forgotPassword: 'ลืมรหัสผ่าน?',
+ userInfo: 'ข้อมูลผู้ใช้',
+ welcome: 'ยินดีต้อนรับ',
+ lastLoginTime: 'เวลาเข้าสู่ระบบล่าสุด',
+ lastLoginIp: 'IP เข้าสู่ระบบล่าสุด'
}
}
@@ -909,6 +1109,46 @@ const vie = {
currencySGD: 'Đô La Singapore',
currencyHKD: 'Đô La Hồng Kông',
currencyPHP: 'Peso Philippines'
+ },
+ // 用户相关
+ user: {
+ login: 'Đăng nhập',
+ register: 'Đăng ký',
+ logout: 'Đăng xuất',
+ username: 'Tên đăng nhập',
+ password: 'Mật khẩu',
+ nickName: 'Tên hiển thị',
+ phone: 'Số điện thoại',
+ email: 'Email',
+ storeCode: 'Mã cửa hàng',
+ loginTitle: 'Đăng nhập người dùng',
+ registerTitle: 'Đăng ký người dùng',
+ loginSuccess: 'Đăng nhập thành công',
+ registerSuccess: 'Đăng ký thành công',
+ logoutSuccess: 'Đăng xuất thành công',
+ usernameRequired: 'Vui lòng nhập tên đăng nhập',
+ passwordRequired: 'Vui lòng nhập mật khẩu',
+ storeCodeRequired: 'Vui lòng nhập mã cửa hàng',
+ usernamePlaceholder: 'Vui lòng nhập tên đăng nhập (3-50 ký tự)',
+ passwordPlaceholder: 'Vui lòng nhập mật khẩu (6-20 ký tự)',
+ nickNamePlaceholder: 'Vui lòng nhập tên hiển thị (tùy chọn)',
+ phonePlaceholder: 'Vui lòng nhập số điện thoại (tùy chọn)',
+ emailPlaceholder: 'Vui lòng nhập email (tùy chọn)',
+ storeCodePlaceholder: 'Vui lòng nhập mã cửa hàng',
+ usernameInvalid: 'Tên đăng nhập chỉ có thể chứa chữ cái, số và dấu gạch dưới',
+ passwordInvalid: 'Độ dài mật khẩu phải từ 6-20 ký tự',
+ phoneInvalid: 'Định dạng số điện thoại không hợp lệ',
+ emailInvalid: 'Định dạng email không hợp lệ',
+ noAccount: 'Chưa có tài khoản?',
+ hasAccount: 'Đã có tài khoản?',
+ goRegister: 'Đăng ký ngay',
+ goLogin: 'Đăng nhập ngay',
+ rememberMe: 'Ghi nhớ đăng nhập',
+ forgotPassword: 'Quên mật khẩu?',
+ userInfo: 'Thông tin người dùng',
+ welcome: 'Chào mừng',
+ lastLoginTime: 'Thời gian đăng nhập cuối',
+ lastLoginIp: 'IP đăng nhập cuối'
}
}
@@ -1015,6 +1255,46 @@ const id = {
currencySGD: 'Dolar Singapura',
currencyHKD: 'Dolar Hong Kong',
currencyPHP: 'Peso Filipina'
+ },
+ // 用户相关
+ user: {
+ login: 'Masuk',
+ register: 'Daftar',
+ logout: 'Keluar',
+ username: 'Nama Pengguna',
+ password: 'Kata Sandi',
+ nickName: 'Nama Panggilan',
+ phone: 'Telepon',
+ email: 'Email',
+ storeCode: 'Kode Toko',
+ loginTitle: 'Masuk Pengguna',
+ registerTitle: 'Pendaftaran Pengguna',
+ loginSuccess: 'Berhasil masuk',
+ registerSuccess: 'Pendaftaran berhasil',
+ logoutSuccess: 'Berhasil keluar',
+ usernameRequired: 'Silakan masukkan nama pengguna',
+ passwordRequired: 'Silakan masukkan kata sandi',
+ storeCodeRequired: 'Silakan masukkan kode toko',
+ usernamePlaceholder: 'Silakan masukkan nama pengguna (3-50 karakter)',
+ passwordPlaceholder: 'Silakan masukkan kata sandi (6-20 karakter)',
+ nickNamePlaceholder: 'Silakan masukkan nama panggilan (opsional)',
+ phonePlaceholder: 'Silakan masukkan nomor telepon (opsional)',
+ emailPlaceholder: 'Silakan masukkan email (opsional)',
+ storeCodePlaceholder: 'Silakan masukkan kode toko',
+ usernameInvalid: 'Nama pengguna hanya dapat berisi huruf, angka dan garis bawah',
+ passwordInvalid: 'Panjang kata sandi harus antara 6-20 karakter',
+ phoneInvalid: 'Format nomor telepon tidak valid',
+ emailInvalid: 'Format email tidak valid',
+ noAccount: 'Belum punya akun?',
+ hasAccount: 'Sudah punya akun?',
+ goRegister: 'Daftar sekarang',
+ goLogin: 'Masuk sekarang',
+ rememberMe: 'Ingat saya',
+ forgotPassword: 'Lupa kata sandi?',
+ userInfo: 'Informasi Pengguna',
+ welcome: 'Selamat datang',
+ lastLoginTime: 'Waktu Masuk Terakhir',
+ lastLoginIp: 'IP Masuk Terakhir'
}
}
diff --git a/src/router/index.js b/src/router/index.js
index f87e09f..796d9dd 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -4,6 +4,9 @@ import Checkout from '../views/Checkout.vue'
import PaymentResult from '../views/PaymentResult.vue'
import OrderQuery from '../views/OrderQuery.vue'
import ProductDetail from '../views/ProductDetail.vue'
+import Login from '../views/Login.vue'
+import Register from '../views/Register.vue'
+import userStore from '../store/user'
const routes = [
{
@@ -11,6 +14,18 @@ const routes = [
name: 'Home',
redirect: '/manage/product'
},
+ {
+ path: '/login',
+ name: 'Login',
+ component: Login,
+ meta: { requiresAuth: false }
+ },
+ {
+ path: '/register',
+ name: 'Register',
+ component: Register,
+ meta: { requiresAuth: false }
+ },
{
path: '/product/:id',
name: 'ProductDetail',
@@ -66,12 +81,20 @@ const routes = [
{
path: '/manage/product',
name: 'ProductManage',
- component: () => import('../views/ProductManage.vue')
+ component: () => import('../views/ProductManage.vue'),
+ meta: { requiresAuth: true }
},
{
path: '/manage/product/create',
name: 'ProductCreate',
- component: () => import('../views/ProductCreate.vue')
+ component: () => import('../views/ProductCreate.vue'),
+ meta: { requiresAuth: true }
+ },
+ {
+ path: '/manage/user/profile',
+ name: 'UserProfile',
+ component: () => import('../views/UserProfile.vue'),
+ meta: { requiresAuth: true }
}
]
@@ -80,9 +103,29 @@ const router = createRouter({
routes
})
-// 添加路由守卫,用于调试
+// 路由守卫
router.beforeEach((to, from, next) => {
console.log('路由导航:', from.path, '->', to.path)
+
+ // 检查是否需要认证
+ if (to.meta.requiresAuth) {
+ // 检查是否已登录
+ if (!userStore.state.isLoggedIn) {
+ // 未登录,跳转到登录页,并保存目标路由
+ next({
+ path: '/login',
+ query: { redirect: to.fullPath }
+ })
+ return
+ }
+ }
+
+ // 如果已登录且访问登录/注册页,重定向到首页
+ if (userStore.state.isLoggedIn && (to.path === '/login' || to.path === '/register')) {
+ next('/manage/product')
+ return
+ }
+
next()
})
diff --git a/src/views/OrderConfirm.vue b/src/views/OrderConfirm.vue
index f12771d..7c433cf 100644
--- a/src/views/OrderConfirm.vue
+++ b/src/views/OrderConfirm.vue
@@ -314,13 +314,17 @@ const loadOrder = async () => {
await loadTranslationByCurrency(order.value.currency)
}
- // 如果订单未支付且需要货币转换,提前计算货币转换信息
+ // 如果订单未支付且需要货币转换,提前计算货币转换信息(页面加载时就转换)
if (order.value.paymentStatus === 'UNPAID' && order.value.currency) {
- // 检查是否需要货币转换(PayPal支持的货币列表)
- const supportedCurrencies = ['USD', 'EUR', 'GBP', 'AUD', 'CAD', 'JPY', 'CNY', 'HKD', 'SGD', 'NZD',
+ // PayPal支持的货币列表(注意:CNY和MYR不支持,需要转换为USD)
+ const supportedCurrencies = ['USD', 'EUR', 'GBP', 'AUD', 'CAD', 'JPY', 'HKD', 'SGD', 'NZD',
'CHF', 'SEK', 'NOK', 'DKK', 'PLN', 'MXN', 'BRL', 'INR', 'KRW', 'THB']
- const needsConversion = !supportedCurrencies.includes(order.value.currency)
+ // 检查是否需要货币转换:
+ // 1. CNY和MYR必须转换为USD(PayPal不支持)
+ // 2. 其他不支持的货币也需要转换
+ const mustConvert = ['CNY', 'MYR'].includes(order.value.currency)
+ const needsConversion = mustConvert || !supportedCurrencies.includes(order.value.currency)
// 如果订单还没有货币转换信息,或者需要更新,则计算
if (needsConversion && (!order.value.paymentCurrency || order.value.paymentCurrency === order.value.currency)) {
@@ -340,11 +344,12 @@ const loadOrder = async () => {
order.value.paymentAmount = conversion.paymentAmount
order.value.exchangeRate = conversion.exchangeRate
order.value.rateLockedAt = conversion.rateLockedAt
- console.log('货币转换信息已计算并更新,支付状态:', order.value.paymentStatus)
+ console.log('货币转换信息已计算并更新(页面加载时),原始货币:', order.value.currency,
+ '支付货币:', order.value.paymentCurrency, '支付金额:', order.value.paymentAmount)
}
} catch (error) {
- console.warn('计算货币转换信息失败:', error)
- // 不显示错误,因为不影响订单显示
+ console.error('计算货币转换信息失败:', error)
+ ElMessage.warning('计算货币转换信息失败,请稍后重试')
}
}
}
@@ -377,6 +382,9 @@ const handlePay = async () => {
try {
// 步骤2:构建PayPal订单创建请求
+ // 注意:如果已经有货币转换信息(页面加载时已计算),使用转换后的货币和金额
+ // 后端会根据currencyCode自动进行转换(CNY/MYR转USD),但这里使用原始货币和金额
+ // 后端会返回转换后的信息,前端显示时使用order.value.paymentCurrency和paymentAmount
const paypalOrderData = {
intent: 'CAPTURE', // 立即捕获
referenceId: order.value.orderNo, // ERP订单号
@@ -407,25 +415,19 @@ const handlePay = async () => {
const paypalOrder = responseData.paypalOrder || responseData // 兼容新旧格式
const currencyConversion = responseData.currencyConversion
- // 如果有货币转换信息,更新订单显示
+ // 如果有货币转换信息,更新订单显示(如果页面加载时已经计算过,这里只是确认)
if (currencyConversion) {
- order.value = {
- ...order.value,
- originalCurrency: currencyConversion.originalCurrency,
- originalAmount: currencyConversion.originalAmount,
- paymentCurrency: currencyConversion.paymentCurrency,
- paymentAmount: currencyConversion.paymentAmount,
- exchangeRate: currencyConversion.exchangeRate,
- rateLockedAt: currencyConversion.rateLockedAt
- }
+ // 更新订单显示信息(保留原有字段,只更新货币转换相关字段)
+ order.value.originalCurrency = currencyConversion.originalCurrency
+ order.value.originalAmount = currencyConversion.originalAmount
+ order.value.paymentCurrency = currencyConversion.paymentCurrency
+ order.value.paymentAmount = currencyConversion.paymentAmount
+ order.value.exchangeRate = currencyConversion.exchangeRate
+ order.value.rateLockedAt = currencyConversion.rateLockedAt
- // 显示货币转换提示
- if (currencyConversion.conversionRequired) {
- ElMessage.info({
- message: `${t('confirm.willPayIn', [getCurrencyName(currencyConversion.paymentCurrency)])},${t('confirm.actualCost')}:${currencyConversion.paymentCurrency} ${formatPrice(currencyConversion.paymentAmount)}`,
- duration: 5000
- })
- }
+ // 如果页面加载时已经显示过转换信息,这里不需要再次提示
+ // 只在首次转换时显示提示(但页面加载时已经计算过了,所以这里通常不需要显示)
+ console.log('PayPal订单创建成功,货币转换信息已确认,支付货币:', currencyConversion.paymentCurrency)
}
// 步骤3-4:获取approval_url并跳转到PayPal登录页
diff --git a/src/views/PayPalCancel.vue b/src/views/PayPalCancel.vue
index 0657c8e..bb22169 100644
--- a/src/views/PayPalCancel.vue
+++ b/src/views/PayPalCancel.vue
@@ -34,6 +34,7 @@ import { ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { getOrderByOrderNo } from '../api/order'
+import { getProductUrl } from '../api/product'
import { formatAmount } from '../utils/helpers'
const router = useRouter()
@@ -55,9 +56,38 @@ const continuePay = () => {
}
}
-// 返回首页
-const goHome = () => {
- router.push('/')
+// 返回商品详情页
+const goHome = async () => {
+ if (order.value && order.value.productId) {
+ try {
+ // 获取商品链接
+ const urlResponse = await getProductUrl(order.value.productId)
+ if (urlResponse.code === '0000' && urlResponse.data) {
+ const productUrl = urlResponse.data
+ // 从完整URL中提取链接码(URL格式:/product/link/xxxxx)
+ const linkCodeMatch = productUrl.match(/\/product\/link\/([^\/]+)/)
+ if (linkCodeMatch && linkCodeMatch[1]) {
+ // 使用链接码跳转到商品详情页
+ router.push(`/product/link/${linkCodeMatch[1]}`)
+ return
+ }
+ // 如果URL格式是 /product/id,直接使用
+ const idMatch = productUrl.match(/\/product\/(\d+)/)
+ if (idMatch && idMatch[1]) {
+ router.push(`/product/${idMatch[1]}`)
+ return
+ }
+ }
+ } catch (error) {
+ console.error('获取商品链接失败,使用productId跳转:', error)
+ }
+ // 如果获取链接失败或无法解析,直接使用productId跳转到商品详情页
+ router.push(`/product/${order.value.productId}`)
+ } else {
+ // 如果没有订单信息或productId,仍然尝试跳转到商品详情页(使用默认方式)
+ // 这种情况理论上不应该发生,因为订单必须关联商品
+ console.warn('订单缺少productId,无法跳转到商品详情页')
+ }
}
// 加载订单信息
diff --git a/src/views/PayPalSuccess.vue b/src/views/PayPalSuccess.vue
index e4e690a..9982d63 100644
--- a/src/views/PayPalSuccess.vue
+++ b/src/views/PayPalSuccess.vue
@@ -70,6 +70,7 @@ import { ElMessage } from 'element-plus'
import { Loading } from '@element-plus/icons-vue'
import { getOrderByOrderNo } from '../api/order'
import { getPayPalOrder, capturePayPalOrder } from '../api/paypal'
+import { getProductUrl } from '../api/product'
import { formatAmount } from '../utils/helpers'
const router = useRouter()
@@ -94,9 +95,38 @@ const viewOrder = () => {
}
}
-// 返回首页
-const goHome = () => {
- router.push('/')
+// 返回商品详情页
+const goHome = async () => {
+ if (order.value && order.value.productId) {
+ try {
+ // 获取商品链接
+ const urlResponse = await getProductUrl(order.value.productId)
+ if (urlResponse.code === '0000' && urlResponse.data) {
+ const productUrl = urlResponse.data
+ // 从完整URL中提取链接码(URL格式:/product/link/xxxxx)
+ const linkCodeMatch = productUrl.match(/\/product\/link\/([^\/]+)/)
+ if (linkCodeMatch && linkCodeMatch[1]) {
+ // 使用链接码跳转到商品详情页
+ router.push(`/product/link/${linkCodeMatch[1]}`)
+ return
+ }
+ // 如果URL格式是 /product/id,直接使用
+ const idMatch = productUrl.match(/\/product\/(\d+)/)
+ if (idMatch && idMatch[1]) {
+ router.push(`/product/${idMatch[1]}`)
+ return
+ }
+ }
+ } catch (error) {
+ console.error('获取商品链接失败,使用productId跳转:', error)
+ }
+ // 如果获取链接失败或无法解析,直接使用productId跳转到商品详情页
+ router.push(`/product/${order.value.productId}`)
+ } else {
+ // 如果没有订单信息或productId,仍然尝试跳转到商品详情页(使用默认方式)
+ // 这种情况理论上不应该发生,因为订单必须关联商品
+ console.warn('订单缺少productId,无法跳转到商品详情页')
+ }
}
// 处理PayPal支付回调(步骤7-11)
diff --git a/src/views/ProductManage.vue b/src/views/ProductManage.vue
index c7a8cd4..848fafb 100644
--- a/src/views/ProductManage.vue
+++ b/src/views/ProductManage.vue
@@ -4,10 +4,16 @@
@@ -124,7 +130,7 @@
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
-import { Plus } from '@element-plus/icons-vue'
+import { Plus, User } from '@element-plus/icons-vue'
import { getProductList, getProductUrl } from '../api/product'
import { formatAmount } from '../utils/helpers'
@@ -137,6 +143,11 @@ const goToCreate = () => {
router.push('/manage/product/create')
}
+// 跳转到用户信息页面
+const goToUserProfile = () => {
+ router.push('/manage/user/profile')
+}
+
// 格式化价格
const formatPrice = (price) => {
return formatAmount(price)
@@ -186,24 +197,8 @@ const loadProductList = async () => {
try {
const response = await getProductList()
if (response.code === '0000' && response.data) {
- // 为每个商品获取链接URL
- const productsWithUrl = await Promise.all(
- response.data.map(async (product) => {
- try {
- const urlResponse = await getProductUrl(product.id)
- if (urlResponse.code === '0000' && urlResponse.data && urlResponse.data.url) {
- product.productUrl = urlResponse.data.url
- } else {
- product.productUrl = ''
- }
- } catch (error) {
- console.error(`获取商品${product.id}链接失败:`, error)
- product.productUrl = ''
- }
- return product
- })
- )
- productList.value = productsWithUrl
+ // 后端已经返回了productUrl,直接使用
+ productList.value = response.data
} else {
ElMessage.error(response.message || '获取商品列表失败')
productList.value = []
@@ -228,22 +223,33 @@ const editProduct = (id) => {
const copyProductUrl = async (id, url) => {
try {
let urlToCopy = url
- // 如果没有URL,先获取
+ // 如果没有URL,尝试获取(降级方案)
if (!urlToCopy) {
- const response = await getProductUrl(id)
- if (response.code === '0000' && response.data && response.data.url) {
- urlToCopy = response.data.url
- // 更新列表中的URL
- const product = productList.value.find(p => p.id === id)
- if (product) {
- product.productUrl = urlToCopy
+ try {
+ const response = await getProductUrl(id)
+ if (response.code === '0000' && response.data && response.data.url) {
+ urlToCopy = response.data.url
+ // 更新列表中的URL
+ const product = productList.value.find(p => p.id === id)
+ if (product) {
+ product.productUrl = urlToCopy
+ }
+ } else {
+ ElMessage.error('获取商品链接失败')
+ return
}
- } else {
+ } catch (error) {
+ console.error('获取商品链接失败:', error)
ElMessage.error('获取商品链接失败')
return
}
}
+ if (!urlToCopy) {
+ ElMessage.error('商品链接为空')
+ return
+ }
+
// 复制到剪贴板
await navigator.clipboard.writeText(urlToCopy)
ElMessage.success('商品链接已复制到剪贴板')
@@ -296,6 +302,11 @@ onMounted(() => {
font-weight: bold;
}
+.header-actions {
+ display: flex;
+ gap: 10px;
+}
+
/* 商品名称单元格 */
.product-name-cell {
display: flex;