|
|
<template> <div class="app-container">
<!-- 消息通知 --> <div class="info-container"> <div class="card-container"> <div class="info-card stat-card" @click="$router.push('/vet-info/VetNotification')" :class="{ shake: tjzs.unreadCount > 0 }" > <img class="info-icon" :src="require('@/assets/images/tongzhi.png')"> <div class="info-content"> <span class="info-title">通知</span> <div class="status-container"> <div class="status-row"> <span class="status-text">未读:{{ tjzs.unreadCount }}</span> </div> <div class="status-row"> <span class="status-text">已读:{{ tjzs.readCount }}</span> </div> </div> </div> </div> <div class="stat-card"> <div class="card-icon icon-total"> <i class="el-icon-message"></i> </div> <div class="card-text"> <div class="card-title">总数</div> <div class="card-value">{{ tjzs.totalCount }}</div> </div> </div> <div class="stat-card"> <div class="card-icon icon-pending"> <i class="el-icon-check"></i> </div> <div class="card-text"> <div class="card-title">已读</div> <div class="card-value">{{ tjzs.readCount }}</div> </div> </div> <div class="stat-card"> <div class="card-icon icon-handled"> <i class="el-icon-bell"></i> </div> <div class="card-text"> <div class="card-title">未读</div> <div class="card-value">{{ tjzs.unreadCount }}</div> </div> </div> </div> </div>
<!-- 资质弹窗 --> <el-dialog :title="title" :visible.sync="flag" width="800px" append-to-body> <el-steps :active="activeStep" finish-status="success" simple style="margin-bottom: 10px;"> <el-step title="选择经营范围"></el-step> <el-step title="上传资质"></el-step> <el-step title="提交审核"></el-step> </el-steps>
<!-- 选择经营范围 --> <div v-if="activeStep === 0"> <el-form ref="qualificationForm" :model="qualificationForm" :rules="rules" label-width="100px"> <el-form-item label="真实姓名" prop="realName"> <el-input v-model="qualificationForm.realName" placeholder="请输入您的真实姓名" /> </el-form-item>
<el-form-item label="身份证号" prop="idCard"> <el-input v-model="qualificationForm.idCard" placeholder="请输入身份证号" /> </el-form-item>
<el-form-item label="资质类型" prop="qualificationType"> <el-select v-model="qualificationForm.qualificationType" placeholder="请选择资质类型" style="width: 100%;" > <el-option v-for="option in qualificationTypeOptions" :key="option.value" :label="option.label" :value="option.value" /> </el-select> </el-form-item> <el-form-item label="证书编号" prop="certificateNo"> <el-input v-model="qualificationForm.certificateNo" placeholder="请输入证书编号" /> </el-form-item> <el-form-item label="经营范围" prop="scopeIds"> <el-select v-model="qualificationForm.scopeIds" multiple placeholder="请选择经营范围" style="width: 100%;" > <el-option v-for="scope in scopeOptions" :key="scope.value" :label="scope.label" :value="scope.value" /> </el-select> <div class="scope-choice"> 已选择:{{ qualificationForm.scopeIds ? qualificationForm.scopeIds.length : 0 }} 项 </div> </el-form-item> </el-form> </div>
<!-- 上传资质 --> <div v-if="activeStep === 1"> <div class="step-summary"> <div class="summary-item"> <span class="summary-label">资质类型:</span> <div class="scope-tags"> <el-tag v-for="qualificationType in qualificationForm.qualificationType" :key="qualificationType" type="info" size="small" class="scope-tag" > {{ getQualificationTypeLabel(qualificationForm.qualificationType) }} </el-tag> </div> </div> <div class="summary-item"> <span class="summary-label">经营范围:</span> <div class="scope-tags"> <el-tag v-for="scopeId in qualificationForm.scopeIds" :key="scopeId" type="info" size="small" class="scope-tag" > {{ getScopeLabel(scopeId) }} </el-tag> </div> </div> </div>
<div style="margin-bottom: 15px; color: #f56c6c; font-size: 14px;"> <i class="el-icon-warning"></i> 需要您上传相关的资质文件: </div>
<el-form> <el-form-item label="资质文件" required> <el-upload ref="upload" action="#" :multiple="true" :file-list="fileList" :auto-upload="false" :on-change="handleFileChange" :on-remove="handleRemove" :show-file-list="true" accept=".jpg,.jpeg,.png,.pdf,.JPG,.JPEG,.PNG,.PDF" > <el-button type="primary">选择文件</el-button> <div slot="tip" class="el-upload__tip"> 请上传清晰的资质文件照片或扫描件(支持JPG、PNG、PDF格式) </div> </el-upload> </el-form-item>
<div v-if="uploadedFiles.length > 0" class="uploaded-files"> <div class="file-list-title">已上传文件:</div> <div v-for="(file, index) in uploadedFiles" :key="index" class="file-item"> <i class="el-icon-document"></i> {{ file.name }} </div> </div> </el-form> </div>
<!-- 提交审核 --> <div v-if="activeStep === 2"> <div style="text-align: center; margin-bottom: 30px;"> <i class="el-icon-circle-check" style="font-size: 60px; color: #67C23A;"></i> <div style="margin-top: 15px; font-size: 16px; color: #303133;"> 请确认信息无误后提交审核 </div> </div>
<div style="background: #f8f9fa; padding: 15px; border-radius: 4px; margin-bottom: 20px;"> <div style="margin-bottom: 8px;"><strong>基本信息</strong></div> <div style="margin-bottom: 5px;">真实姓名:{{ qualificationForm.realName }}</div> <div style="margin-bottom: 5px;">身份证号:{{ qualificationForm.idCard }}</div> <div style="margin-bottom: 5px;">资质类型:{{ qualificationForm.qualificationType }}</div> <div>证书编号:{{ qualificationForm.certificateNo }}</div> </div>
<div style="margin-bottom: 15px; color: #606266;"> <div>经营范围:</div> <div class="scope-tags"> <el-tag v-for="scopeId in qualificationForm.scopeIds" :key="scopeId" type="info" size="medium" class="scope-tag" > {{ getScopeLabel(scopeId) }} </el-tag> </div> </div>
<div style="background: #f8f9fa; padding: 15px; border-radius: 4px; margin-bottom: 20px;"> <div style="margin-bottom: 8px;"><strong>上传文件</strong></div> <div v-if="fileList.length > 0"> <div v-for="(file, index) in fileList" :key="index" style="margin-bottom: 5px;"> {{ file.name }} </div> </div> <div v-else style="color: #909399;">暂无文件</div> </div> </div>
<!-- 操作按钮 --> <div slot="footer" class="dialog-footer"> <div v-if="activeStep > 0" style="float: left;"> <el-button @click="prevStep">上一步</el-button> </div>
<div v-if="activeStep < 2"> <el-button type="primary" @click="nextStep"> {{ activeStep === 1 ? '下一步' : '下一步' }} </el-button> </div>
<div v-if="activeStep === 2"> <el-button @click="prevStep">上一步</el-button> <el-button type="primary" @click="submitQualification" :loading="loading"> 提交审核 </el-button> </div> </div> </el-dialog> </div></template>
<script>import { submitAuditQualification, qualificationAudit, getQualificationTypeOptions, getScopeOptions, uploadQualification } from "../api/vet/qualification";import { getStatsCard } from "../api/vet/notification";
export default { name: "Syd", data() { return { // 消息通知
tjzs: {},
// 资质弹窗
title: "兽医资质审核", flag: true, open: false, loading: false, activeStep: 0, qualificationForm: { qualificationId: "", realName: '', idCard: '', qualificationType: '', certificateNo: '', scopeIds: [], certificateFiles: '', }, fileList: [], scopeOptions: [],
uploadedFiles: [], qualificationTypeOptions: [], selectedScopesDetail: '', fileUploadResponses: [],
rules: { realName: [ {required: true, message: "请输入真实姓名", trigger: "blur"} ], idCard: [ {required: true, message: "请输入身份证号", trigger: "blur"} ], qualificationType: [ {required: true, message: "请选择资质类型", trigger: "change"} ], certificateNo : [ {required: true, message: "请输入证书编号", trigger: "blur"} ], scopeIds: [ { required: true, type: 'array', message: "请选择至少一个经营范围", trigger: "change" } ] } } }, created() { this.initOptions(); this.getcountSummary() }, methods: { // 获取通知数据
getcountSummary() { getStatsCard().then(res => { this.tjzs = res.data }) },
// 初始化选项
initOptions() { const fetchData = async () => { const qtypeRes = await getQualificationTypeOptions(); this.qualificationTypeOptions = qtypeRes.data;
const scopeRes = await getScopeOptions(); this.scopeOptions = scopeRes.data; } fetchData(); },
// 获取资质类型标签
getQualificationTypeLabel(value) { const option = this.qualificationTypeOptions.find(item => item.value === value); return option ? option.label : value; },
// 获取经营范围标签
getScopeLabel(scopeId) { const scope = this.scopeOptions.find(item => item.value === scopeId); return scope ? scope.label : scopeId; },
// 文件上传
uploadSingleFile(file) { return new Promise((resolve, reject) => { const formData = new FormData(); formData.append('file', file.raw);
uploadQualification(formData) .then(response => { const filePath = response.data.filePath || response.data.path || response.data.url; this.fileUploadResponses.push({ fileName: file.name, filePath: filePath, fileId: response.data.id || response.data.fileId }); this.uploadedFiles.push({ name: file.name, uid: file.uid, path: filePath }); this.$message.success(`${file.name} 上传成功`); resolve(response); }) .catch(error => { reject(error); }); }); },
// 文件改变
handleFileChange(file, fileList) { if (!file.raw || file.raw.size / 1024 / 1024 >= 10 || !['image/jpeg', 'image/png', 'application/pdf'].includes(file.raw.type)) { let errorMsg = ''; if (!file.raw) errorMsg = '获取文件失败,请重新选择文件'; else if (file.raw.size / 1024 / 1024 >= 10) errorMsg = '文件大小不能超过10MB'; else errorMsg = '只能上传JPG、PNG或PDF格式的文件'; this.$message.error(errorMsg); this.fileList = fileList.filter(item => item.uid !== file.uid); return; } this.fileList = fileList; if (file.status === 'ready') { file.status = 'uploading'; this.uploadSingleFile(file) .then(() => file.status = 'success') .catch(() => file.status = 'fail'); } },
// 文件移除
handleRemove(file, fileList) { this.fileList = fileList; const showIndex = this.uploadedFiles.findIndex(f => f.name === file.name); if (showIndex > -1) { this.uploadedFiles.splice(showIndex, 1); } const resIndex = this.fileUploadResponses.findIndex(f => f.fileName === file.name); if (resIndex > -1) { this.fileUploadResponses.splice(resIndex, 1); } },
// 上一步
prevStep() { if (this.activeStep > 0) { this.activeStep-- } },
// 下一步
nextStep() { if (this.activeStep === 0) { this.$refs.qualificationForm.validate(valid => valid && (this.activeStep++)); return; } if (this.activeStep === 1) { if (!this.fileList.length) { this.$message.warning('请上传资质文件'); return; } const anyUploading = this.fileList.some(file => file.status === 'uploading'); const anyFailed = this.fileList.some(file => file.status === 'fail'); const allSuccess = this.fileList.every(file => file.status === 'success'); if (anyUploading) { this.$message.warning('文件正在上传中,请等待上传完成'); } else if (anyFailed) { this.$message.warning('存在上传失败的文件,请重新上传或移除失败的文件'); } else if (!allSuccess) { this.$message.warning('请确保所有文件都上传成功'); } else { this.activeStep++; } } },
// 提交审核
submitQualification() { this.loading = true;
const requestData = { realName: this.qualificationForm.realName, idCard: this.qualificationForm.idCard, qualificationType: this.qualificationForm.qualificationType, certificateNo: this.qualificationForm.certificateNo, scopeIds: this.qualificationForm.scopeIds || [], certificateFiles: this.fileUploadResponses.map(item => item.fileUrl).join(','), certificateFileIds: this.fileUploadResponses.map(item => item.fileId).join(',') }; // console.log('提交的数据:', JSON.stringify(requestData, null, 2));
submitAuditQualification(requestData) .then(response => { this.$modal.msgSuccess('提交成功,请等待审核'); this.flag = false; this.resetForm(); }) .catch(error => { console.error('提交失败:', error); console.error('错误详情:', error.response?.data); if (error.response?.data?.message) { this.$message.error('提交失败: ' + error.response.data.message); } else { this.$message.error('提交失败,请重试'); } }) .finally(() => { this.loading = false; }); } }}</script>
<style scoped lang="scss">/* 轮播背景 */.card-container { display: flex; gap: 20px; padding: 20px;}
.stat-card { flex: 1; min-width: 0; padding: 30px 20px; border: 1px solid #e5e6eb; border-radius: 16px; background: #fff; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); cursor: pointer; transition: all 0.3s ease; min-height: 180px; transform-origin: center center;
&:hover { transform: translateY(-5px); box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); }}
.info-card.stat-card { background-color: #bbdefb; color: #0d47a1; display: flex; align-items: center; justify-content: space-around; padding: 20px;}
.shake { animation: cardShake 1s infinite linear; background-color: #fffde6 !important; &:hover { animation-play-state: paused; background-color: #edeacd !important; }}
.info-card .info-icon { width: 60%; cursor: pointer;}
.info-card .info-title { font-size: 28px; font-weight: bold; color: #42B983; margin-bottom: 0; text-align: center; cursor: pointer;}
.card-icon { width: 60px; height: 60px; border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-right: 20px;}
.card-icon i { font-size: 30px; color: #fff;}
.card-text { display: flex; flex-direction: column; justify-content: center; align-items: center;}
.card-title { font-size: 16px; color: #333; margin-bottom: 8px;}
.card-value { font-size: 24px; font-weight: bold; color: #333;}
.icon-total { background: linear-gradient(135deg, #ff9a44 0%, #ff6b08 100%);}.icon-pending { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);}.icon-handled { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);}
/* 资质弹窗 */
/* 经营范围 */.scope-choice { color: #b6bbc6; font-size: 12px; margin-top: 5px;}
.step-summary { margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 4px;}
.summary-item { display: flex; align-items: flex-start; margin-bottom: 10px;
&:last-child { margin-bottom: 0; }}
.summary-label { min-width: 80px; color: #606266; font-weight: bold;}
.summary-value { color: #303133;}
.scope-tags { display: flex; flex-wrap: wrap; gap: 5px;}
.scope-tag { margin-right: 5px; margin-bottom: 5px;}
.uploaded-files { margin-top: 15px; padding: 10px; background: #f5f7fa; border-radius: 4px;}
.file-list-title { font-weight: bold; margin-bottom: 8px; color: #303133;}
.file-item { padding: 5px 0; color: #606266; display: flex; align-items: center;
i { margin-right: 5px; color: #409EFF; }}
/* 响应式设计 */@media (max-width: 768px) { .card-container { flex-direction: column; padding: 10px; gap: 15px; }
.stat-card { min-height: 150px; padding: 20px 15px; }
.card-icon { width: 50px; height: 50px; margin-right: 15px;
i { font-size: 24px; } }
.info-card .info-icon { width: 40px; height: 40px; }
.info-card .info-title { font-size: 20px; }
.card-value { font-size: 20px; }}
/* 抖动动画 */@keyframes cardShake { 0% { transform: translateX(0); } 10% { transform: translateX(-5px) translateY(-2px); } 20% { transform: translateX(5px) translateY(2px); } 30% { transform: translateX(-5px) translateY(-2px); } 40% { transform: translateX(5px) translateY(2px); } 50% { transform: translateX(-3px) translateY(-1px); } 60% { transform: translateX(3px) translateY(1px); } 70% { transform: translateX(-3px) translateY(-1px); } 80% { transform: translateX(3px) translateY(1px); } 90% { transform: translateX(-2px) translateY(0); } 100% { transform: translateX(0); }}
.shake { animation: cardShake 1s infinite linear; &:hover { animation-play-state: paused; }}</style>
|