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;