From 4b27db42d312b61cfd08701872d9f1c7d46ec0a5 Mon Sep 17 00:00:00 2001 From: 18631081161 <2088094923@qq.com> Date: Mon, 22 Dec 2025 18:11:31 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=90=8E=E5=8F=B0=E4=BF=AE?= =?UTF-8?q?=E6=94=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/src/views/appointments/index.vue | 157 ++++++++++++++++-- admin/src/views/services/index.vue | 23 +-- admin/src/views/users/index.vue | 28 ---- backend/src/middleware/responseFormatter.js | 11 +- .../src/services/adminAppointmentService.js | 4 +- 5 files changed, 163 insertions(+), 60 deletions(-) diff --git a/admin/src/views/appointments/index.vue b/admin/src/views/appointments/index.vue index a6f5ebd..2db18e6 100644 --- a/admin/src/views/appointments/index.vue +++ b/admin/src/views/appointments/index.vue @@ -70,8 +70,8 @@ - {{ row.user?.nickname || row.user?.realName || '-' }} - {{ row.user?.phone || '-' }} + {{ row.user?.nickname || '-' }} + UID: {{ row.user?.uid || '-' }} @@ -137,7 +137,7 @@ {{ formatDate(row.createdAt) }} - + @@ -147,6 +147,10 @@ 状态 + + + 收款 + @@ -512,19 +516,16 @@ {{ appointmentDetails.user.nickname || '-' }} - - {{ appointmentDetails.user.realName || '-' }} + + {{ appointmentDetails.user.uid || '-' }} - - {{ appointmentDetails.user.phone || '-' }} - - - {{ appointmentDetails.user.whatsapp || '-' }} - - - {{ appointmentDetails.user.wechatId || '-' }} - - + + + 语言偏好 + + + + {{ getLanguageLabel(appointmentDetails.user.language) }} @@ -578,6 +579,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 取消 + + 创建支付订单 + + + @@ -633,6 +686,33 @@ const statusOptions = [ { value: 'cancelled', label: '已取消' } ] +// Payment order dialog state +const paymentDialogVisible = ref(false) +const paymentCreating = ref(false) +const paymentFormRef = ref(null) +const paymentForm = reactive({ + appointmentId: '', + appointmentNo: '', + userId: '', + userNickname: '', + serviceContent: '', + amount: 0, + paymentTime: '', + notes: '' +}) +const paymentRules = { + serviceContent: [ + { required: true, message: '请输入服务内容', trigger: 'blur' } + ], + amount: [ + { required: true, message: '请输入金额', trigger: 'blur' }, + { type: 'number', min: 0.01, message: '金额必须大于0', trigger: 'blur' } + ], + paymentTime: [ + { required: true, message: '请选择支付时间', trigger: 'change' } + ] +} + // Fetch categories for filter async function fetchCategories() { try { @@ -808,6 +888,51 @@ async function confirmUpdateStatus() { } } +// Handle create payment order +function handleCreatePaymentOrder(row) { + paymentForm.appointmentId = row.id + paymentForm.appointmentNo = row.appointmentNo + paymentForm.userId = row.user?.id || row.userId + paymentForm.userNickname = `${row.user?.nickname || '-'} (UID: ${row.user?.uid || '-'})` + paymentForm.serviceContent = row.service?.titleZh || row.hotService?.name_zh || row.serviceType || '' + paymentForm.amount = parseFloat(row.amount) || 0 + // 默认设置为当前时间 + paymentForm.paymentTime = new Date().toISOString().slice(0, 19).replace('T', ' ') + paymentForm.notes = '' + paymentDialogVisible.value = true +} + +// Confirm create payment order +async function confirmCreatePaymentOrder() { + if (!paymentFormRef.value) return + + await paymentFormRef.value.validate(async (valid) => { + if (!valid) return + + paymentCreating.value = true + try { + const response = await api.post('/api/v1/admin/payment-orders', { + userId: paymentForm.userId, + appointmentId: paymentForm.appointmentNo, + amount: paymentForm.amount, + serviceContent: paymentForm.serviceContent, + paymentTime: paymentForm.paymentTime, + notes: paymentForm.notes + }) + + if (response.data.success || response.data.code === 0) { + ElMessage.success('支付订单创建成功') + paymentDialogVisible.value = false + } + } catch (error) { + console.error('Failed to create payment order:', error) + ElMessage.error(error.response?.data?.message || '创建支付订单失败') + } finally { + paymentCreating.value = false + } + }) +} + // Helper functions function formatDate(dateStr) { if (!dateStr) return '-' @@ -1018,7 +1143,7 @@ onMounted(() => { color: #303133; } - .phone { + .uid { font-size: 12px; color: #909399; } diff --git a/admin/src/views/services/index.vue b/admin/src/views/services/index.vue index 4103a62..59ff7d7 100644 --- a/admin/src/views/services/index.vue +++ b/admin/src/views/services/index.vue @@ -72,7 +72,7 @@ - + {{ row.category.nameZh || row.category.name_zh || '-' }} @@ -172,8 +172,8 @@ - - + + 其他信息 @@ -285,7 +285,7 @@ const defaultForm = { serviceType: '', titleZh: '', titleEn: '', - titlePt: '', + titleEs: '', image: '', sortOrder: 0, status: 'active' @@ -299,7 +299,7 @@ const formRules = { serviceType: [{ required: true, message: '请选择服务具体类型', trigger: 'change' }], titleZh: [{ required: true, message: '请输入中文标题', trigger: 'blur' }], titleEn: [{ required: true, message: '请输入英文标题', trigger: 'blur' }], - titlePt: [{ required: true, message: '请输入西语标题', trigger: 'blur' }] + titleEs: [{ required: true, message: '请输入西语标题', trigger: 'blur' }] } // 获取服务类型标签 @@ -338,11 +338,12 @@ async function fetchServices() { const response = await api.get('/api/v1/admin/services', { params }) // Support both old format (success: true) and new format (code: 0) if (response.data.success === true || response.data.code === 0) { - services.value = response.data.data - // Handle pagination from both formats - const paginationData = response.data.pagination || {} - pagination.total = paginationData.total || 0 - pagination.totalPages = paginationData.totalPages || 0 + services.value = response.data.data || [] + // Handle pagination from response + if (response.data.pagination) { + pagination.total = response.data.pagination.total || 0 + pagination.totalPages = response.data.pagination.totalPages || 0 + } } } catch (error) { console.error('Failed to fetch services:', error) @@ -408,7 +409,7 @@ function handleEdit(row) { serviceType: row.serviceType || '', titleZh: row.titleZh, titleEn: row.titleEn, - titlePt: row.titlePt, + titleEs: row.titleEs, image: row.image || '', sortOrder: row.sortOrder || 0, status: row.status diff --git a/admin/src/views/users/index.vue b/admin/src/views/users/index.vue index 0c04385..342cc40 100644 --- a/admin/src/views/users/index.vue +++ b/admin/src/views/users/index.vue @@ -63,21 +63,6 @@ - - - {{ row.realName || '-' }} - - - - - {{ row.phone || '-' }} - - - - - {{ row.wechatId || '-' }} - - @@ -85,11 +70,6 @@ - - - ¥{{ parseFloat(row.balance || 0).toFixed(2) }} - - @@ -151,10 +131,6 @@ {{ userDetails.user.nickname || '-' }} {{ userDetails.user.uid || '-' }} - {{ userDetails.user.realName || '-' }} - {{ userDetails.user.phone || '-' }} - {{ userDetails.user.whatsapp || '-' }} - {{ userDetails.user.wechatId || '-' }} {{ getLanguageLabel(userDetails.user.language) }} @@ -165,10 +141,6 @@ {{ userDetails.user.status === 'active' ? '正常' : '已禁用' }} - - ¥{{ parseFloat(userDetails.user.balance || 0).toFixed(2) }} - - {{ userDetails.user.invitationCode || '-' }} {{ userDetails.inviter ? (userDetails.inviter.nickname || userDetails.inviter.realName) : '-' }} diff --git a/backend/src/middleware/responseFormatter.js b/backend/src/middleware/responseFormatter.js index 4a1d9f3..e42e1e9 100644 --- a/backend/src/middleware/responseFormatter.js +++ b/backend/src/middleware/responseFormatter.js @@ -17,13 +17,18 @@ const responseFormatter = (req, res, next) => { // Transform old format to new format if (body && typeof body === 'object') { - // Success response: { success: true, data: {...} } + // Success response: { success: true, data: {...}, pagination: {...} } if (body.success === true) { - return originalJson({ + const response = { code: 0, message: body.message || 'Success', data: body.data || null, - }); + }; + // Preserve pagination if present + if (body.pagination) { + response.pagination = body.pagination; + } + return originalJson(response); } // Error response: { success: false, error: {...} } diff --git a/backend/src/services/adminAppointmentService.js b/backend/src/services/adminAppointmentService.js index 936aca6..6af415e 100644 --- a/backend/src/services/adminAppointmentService.js +++ b/backend/src/services/adminAppointmentService.js @@ -81,7 +81,7 @@ const getAppointmentList = async (options = {}) => { { model: User, as: 'user', - attributes: ['id', 'nickname', 'realName', 'phone', 'whatsapp', 'wechatId'], + attributes: ['id', 'uid', 'nickname', 'realName', 'phone', 'whatsapp', 'wechatId'], }, ]; @@ -158,7 +158,7 @@ const getAppointmentDetails = async (appointmentId) => { { model: User, as: 'user', - attributes: ['id', 'nickname', 'realName', 'phone', 'whatsapp', 'wechatId', 'language', 'balance'], + attributes: ['id', 'uid', 'nickname', 'realName', 'phone', 'whatsapp', 'wechatId', 'language', 'balance'], }, ], });