You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

710 lines
20 KiB

<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>