From e8439c82c19663378bbe08f0331ed213a404e0dd Mon Sep 17 00:00:00 2001 From: 18631081161 <2088094923@qq.com> Date: Fri, 13 Feb 2026 16:25:48 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/src/views/appointments/index.vue | 115 ++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/admin/src/views/appointments/index.vue b/admin/src/views/appointments/index.vue index daf0460..ed76361 100644 --- a/admin/src/views/appointments/index.vue +++ b/admin/src/views/appointments/index.vue @@ -631,6 +631,22 @@ value-format="YYYY-MM-DD HH:mm:ss" /> + + + + + +
点击上传支付凭证截图(必填)
+
-import { ref, reactive, onMounted } from 'vue' +import { ref, reactive, computed, onMounted } from 'vue' import { ElMessage } from 'element-plus' +import { Plus } from '@element-plus/icons-vue' import api from '@/utils/api' // State @@ -715,6 +732,7 @@ const paymentForm = reactive({ amountPeso: null, amountRmb: null, paymentTime: '', + paymentProof: '', notes: '' }) const paymentRules = { @@ -723,9 +741,60 @@ const paymentRules = { ], paymentTime: [ { required: true, message: '请选择支付时间', trigger: 'change' } + ], + paymentProof: [ + { required: true, message: '请上传支付凭证', trigger: 'change' } ] } +// Upload config +const uploadUrl = computed(() => { + const baseUrl = import.meta.env.VITE_API_BASE_URL || '' + return `${baseUrl}/api/v1/admin/upload/image` +}) + +const uploadHeaders = computed(() => { + const token = localStorage.getItem('admin_token') + return { + Authorization: token ? `Bearer ${token}` : '' + } +}) + +function getImageUrl(path) { + if (!path) return '' + if (path.startsWith('http')) return path + const baseUrl = import.meta.env.VITE_API_BASE_URL || '' + return `${baseUrl}${path}` +} + +function handlePaymentProofSuccess(response) { + if (response.code === 0 && response.data?.url) { + paymentForm.paymentProof = response.data.url + ElMessage.success('上传成功') + } else { + ElMessage.error(response.error?.message || '上传失败') + } +} + +function handlePaymentProofError(error) { + console.error('Upload error:', error) + ElMessage.error('上传失败,请重试') +} + +function beforePaymentProofUpload(file) { + const isImage = file.type.startsWith('image/') + const isLt5M = file.size / 1024 / 1024 < 5 + if (!isImage) { + ElMessage.error('只能上传图片文件') + return false + } + if (!isLt5M) { + ElMessage.error('图片大小不能超过 5MB') + return false + } + return true +} + // Fetch categories for filter async function fetchCategories() { try { @@ -920,6 +989,7 @@ function handleCreatePaymentOrder(row) { const seconds = String(now.getSeconds()).padStart(2, '0') paymentForm.paymentTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` paymentForm.notes = '' + paymentForm.paymentProof = '' paymentDialogVisible.value = true } @@ -935,6 +1005,11 @@ async function confirmCreatePaymentOrder() { ElMessage.warning('请至少填写一种货币金额(比索或人民币)') return } + + if (!paymentForm.paymentProof) { + ElMessage.warning('请上传支付凭证图片') + return + } await paymentFormRef.value.validate(async (valid) => { if (!valid) return @@ -948,6 +1023,7 @@ async function confirmCreatePaymentOrder() { amountRmb: hasRmb ? paymentForm.amountRmb : undefined, serviceContent: paymentForm.serviceContent, paymentTime: paymentForm.paymentTime, + paymentProof: paymentForm.paymentProof, notes: paymentForm.notes }) @@ -1206,6 +1282,43 @@ onMounted(() => { color: #909399; } + .payment-proof-uploader { + :deep(.el-upload) { + border: 1px dashed #d9d9d9; + border-radius: 6px; + cursor: pointer; + position: relative; + overflow: hidden; + transition: border-color 0.3s; + width: 148px; + height: 148px; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + border-color: #409eff; + } + } + } + + .payment-proof-uploader-icon { + font-size: 28px; + color: #8c939d; + } + + .payment-proof-image { + width: 146px; + height: 146px; + object-fit: contain; + } + + .upload-tip { + font-size: 12px; + color: #909399; + margin-top: 8px; + } + .appointment-details { .mt-20 { margin-top: 20px;