<script setup lang="ts"> import { ref } from 'vue' import { showConfirmDialog, showSuccessToast, showToast, UploaderFileListItem, Popup, Picker } from 'vant' const showStatusPicker = ref(false) const showPreview = ref(false) const currentPreviewImage = ref('') const statusMap: { [key: number]: string } = { 1: '待审核', 2: '已通过', 3: '已驳回' } const statusOptions = [ { text: '待审核', value: 1 }, { text: '通过', value: 2 }, { text: '驳回', value: 3 } ] import axios from "axios" import { handleApprovalApi } from '@/common/apis/approval' import type { HandleApprovalRequestData } from '@/common/apis/approval/type' import { useRoute, useRouter } from 'vue-router' import { useApprovalStore } from '@/pinia/stores/approval' const { VITE_APP_BASE_API } = import.meta.env; const router = useRouter() const route = useRoute() const approvalStore = useApprovalStore() const formData = ref<HandleApprovalRequestData>({ approvalId: approvalStore.currentApproval?.approvalId || 0, status: 1, returnAmount: 0, auditImages: '', auditRemark: '' }) const submitting = ref(false) const fileList = ref<UploaderFileListItem[]>([]) const uploading = ref(false) const validateForm = () => { if (!formData.value.approvalId || isNaN(formData.value.approvalId)) { showConfirmDialog({ title: '错误', message: '审批单ID参数错误' }) return false } if (formData.value.returnAmount <= 0) { showConfirmDialog({ title: '提示', message: '退款金额需大于0' }) return false } if (null != approvalStore.currentApproval && formData.value.returnAmount > approvalStore.currentApproval.goodsPrice) { showConfirmDialog({ title: '提示', message: '退款金额不能超过商品价格' }) return false } if (!formData.value.auditImages) { showConfirmDialog({ title: '提示', message: '请上传审核图片' }) return false } if (formData.value.status === 3 && !formData.value.auditRemark) { showConfirmDialog({ title: '提示', message: '驳回时必须填写审核说明' }) return false } return true } const handleFileUpload = async (items: UploaderFileListItem | UploaderFileListItem[]) => { const files = Array.isArray(items) ? items : [items] uploading.value = true try { const uploadPromises = files.map(async (item) => { item.status = 'uploading' item.message = '上传中...' const file = item.file as File const formData = new FormData() formData.append('file', file) const { data } = await axios.post<{ code: number data: { url: string } message?: string }>(VITE_APP_BASE_API + '/file/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }) if (data.code !== 0) { throw new Error(data.message || '文件上传失败') } return { url: data.data.url } }) const urls = await Promise.all(uploadPromises) files.forEach((item, index) => { item.status = 'done' item.message = '上传成功' item.url = urls[index].url }) formData.value.auditImages = fileList.value.map(item => item.url).join(',') } catch (error) { showConfirmDialog({ title: '上传失败', message: error instanceof Error ? error.message : '未知错误' }) } finally { uploading.value = false } } const onStatusConfirm = ({ selectedOptions }: { selectedOptions: { value: number }[] }) => { submitting.value = true try { if (!selectedOptions?.[0]?.value) { throw new Error('请选择有效的审批状态') } formData.value.status = selectedOptions[0].value showStatusPicker.value = false } catch (error) { showConfirmDialog({ title: '操作失败', message: error instanceof Error ? error.message : '状态选择异常' }) } finally { submitting.value = false } } const previewImage = (url: string) => { currentPreviewImage.value = url showPreview.value = true } onMounted(() => { if (!approvalStore.currentApproval) { router.push('/approval/list') } else if (approvalStore.currentApproval.status !== 1) { // 填充历史审批数据 formData.value = { ...formData.value, status: approvalStore.currentApproval.status, returnAmount: approvalStore.currentApproval.returnAmount, auditRemark: approvalStore.currentApproval.auditRemark, auditImages: approvalStore.currentApproval.auditImages } // 处理文件回显 if (approvalStore.currentApproval.auditImages) { fileList.value = approvalStore.currentApproval.auditImages.split(',').map(url => ({ url, status: 'done', message: '已上传' })) } } }) const handleSubmit = async () => { if (!validateForm()) return submitting.value = true try { const { code, msg } = await handleApprovalApi(formData.value) if (code === 0) { showSuccessToast('操作成功') await showConfirmDialog({ title: "操作成功", message: `审批处理已完成` }) router.push('/approval/list') } else { console.error('操作失败code:', code, 'msg:', msg) showConfirmDialog({ title: '操作失败', message: msg || '操作失败' }) } } catch (error) { console.error('提交失败:', error) showConfirmDialog({ title: '提交失败', message: error instanceof Error ? error.message : '网络请求异常' }) } finally { submitting.value = false } } </script> <template> <div class="approval-container"> <van-nav-bar title="审批处理" left-text="返回" left-arrow fixed @click-left="() => router.go(-1)" /> <div class="content-wrapper"> <van-cell-group> <van-cell title="商品名称" :value="approvalStore.currentApproval?.goodsName" /> <van-cell title="商品封面" v-if="approvalStore.currentApproval?.coverImg"> <van-image :src="approvalStore.currentApproval.coverImg" fit="cover" width="80" height="80" @click="previewImage(approvalStore.currentApproval.coverImg)" style="margin-top: 8px" /> </van-cell> <van-cell title="退还数量" :value="approvalStore.currentApproval?.returnQuantity" /> <van-cell title="商品单价" :value="`¥${approvalStore.currentApproval?.goodsPrice}`" /> <van-cell title="当前状态" :value="statusMap[approvalStore.currentApproval?.status || 1]" /> <van-cell title="退还说明" :value="approvalStore.currentApproval?.returnRemark" /> </van-cell-group> <van-cell-group class="image-section"> <van-cell title="退还凭证"> <van-grid :column-num="3" gutter="10"> <van-grid-item v-for="(img, index) in approvalStore.currentApproval?.returnImages.split(',')" :key="index"> <van-image :src="img" fit="cover" @click="previewImage(img)" /> </van-grid-item> </van-grid> </van-cell> </van-cell-group> <van-cell-group> <!-- 原有表单字段保持不变 --> <van-field v-model="formData.returnAmount" :readonly="approvalStore.currentApproval?.status !== 1" label="退款金额" type="number" class="audit-remark-field" :min="0" /> <van-field :model-value="statusOptions.find(opt => opt.value === formData.status)?.text || ''" label="审批结果" readonly @click="showStatusPicker = true" :disabled="approvalStore.currentApproval?.status !== 1" class="clickable-status-field" /> <van-field v-model="formData.auditRemark" label="审核说明" type="textarea" rows="2" autosize class="audit-remark-field" /> </van-cell-group> <van-popup v-model:show="showStatusPicker" position="bottom"> <van-picker :columns="statusOptions" @confirm="onStatusConfirm" @cancel="showStatusPicker = false" /> </van-popup> <van-popup v-model:show="showPreview" position="center" :style="{ width: '90%', height: '80%' }" round> <div class="preview-wrapper"> <van-icon name="cross" class="close-icon" @click="showPreview = false" /> <van-image :src="currentPreviewImage" fit="contain" class="preview-image" /> </div> </van-popup> <van-cell-group class="upload-section"> <van-cell title="审核凭证"> <template #extra> <van-uploader v-model="fileList" multiple max-count="3" :after-read="handleFileUpload" /> </template> </van-cell> </van-cell-group> <div class="submit-bar" v-if="approvalStore.currentApproval?.status !== 2"> <van-button type="primary" block :loading="submitting" loading-text="提交中..." @click="handleSubmit" > 提交审批 </van-button> </div> </div> </div> </template> <style scoped> .approval-container { padding: 12px 16px; } .content-wrapper { padding-top: 46px; } .upload-section { margin: 20px 0; } .preview-wrapper { position: relative; height: 100%; padding: 20px; .close-icon { position: absolute; top: 10px; right: 10px; z-index: 1; font-size: 24px; color: #fff; background: rgba(0, 0, 0, 0.3); border-radius: 50%; padding: 4px; } .preview-image { width: 100%; height: 100%; } } .submit-bar { position: sticky; bottom: 20px; padding: 16px; background: #fff; box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1); border-radius: 8px; margin: 0 16px; z-index: 1; } .content-wrapper { padding-bottom: 100px; } .audit-remark-field::v-deep .van-field__control { background-color: #fffbe6; padding: 8px; border-radius: 4px; } .clickable-status-field:not(:disabled)::v-deep .van-field__control { cursor: pointer; transition: all 0.3s ease; background-color: #f7f8fa; } .clickable-status-field:not(:disabled):hover::v-deep .van-field__control { background-color: #f2f3f5; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); } .clickable-status-field:not(:disabled):active::v-deep .van-field__control { background-color: #ebedf0; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); } </style>