471 lines
13 KiB
Vue
471 lines
13 KiB
Vue
<template>
|
||
<div class="system-menu-container layout-padding">
|
||
<ProTable
|
||
ref="tableRef"
|
||
:request-api="merchantcertificationPageGet"
|
||
@selection-change="handleSelectionChange"
|
||
:columns="columns"
|
||
:pagination="true"
|
||
:tool-button="false"
|
||
:initParams="initParam"
|
||
row-key="id"
|
||
>
|
||
<template #tools>
|
||
<el-button v-auth="'user:general:audit'" size="small" type="primary" @click="showAuditDialog"> 批量审核 </el-button>
|
||
</template>
|
||
<template #index="scope">
|
||
{{ scope.$index + 1 }}
|
||
</template>
|
||
<template #auditStatus="scope">
|
||
<el-tag :type="getAuditStatusType(scope.row.auditStatus)">
|
||
{{ getAuditStatusText(scope.row.auditStatus) }}
|
||
</el-tag>
|
||
</template>
|
||
<template #businessLicenseUrl="scope">
|
||
<PreviewImage v-if="scope.row.businessLicenseUrl" :src="scope.row.businessLicenseUrl" width="100px" height="100px" fit="cover" radius="4px" :show-zoom-icon="true" />
|
||
<span v-else>-</span>
|
||
</template>
|
||
<template #action="scope">
|
||
<el-button v-auth="'user:general:record'" size="small" text type="primary" @click="onOpenMenu(scope.row)"> 参赛记录 </el-button>
|
||
</template>
|
||
</ProTable>
|
||
|
||
<!-- 参赛记录弹窗 -->
|
||
<el-dialog title="参赛记录" v-model="isShowDialog" width="80%" @close="onDialogClose">
|
||
<ProTable v-if="isShowDialog" ref="table" :request-api="eventrecordPageGet" :columns="columnsUser" :pagination="true" :tool-button="false" :initParam="initParamUser" preserve-expanded-content>
|
||
<!-- 展开列 -->
|
||
<el-table-column type="expand">
|
||
<template #default="scope">
|
||
<div class="expand-content">
|
||
<el-table :data="scope.row.items || []" border style="width: 100%; margin-top: 10px;">
|
||
<el-table-column label="项目名称" prop="projectName" min-width="150" />
|
||
<el-table-column label="角色" prop="role" width="120" />
|
||
<el-table-column label="参赛组别" prop="groupName" width="120" />
|
||
<el-table-column label="姓名" prop="participantName" min-width="200" />
|
||
<el-table-column label="成绩" prop="score" width="120" />
|
||
<el-table-column label="名次" prop="rank" width="100" />
|
||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||
<template #default="item">
|
||
<el-button
|
||
v-if="item.row.certificateUrl"
|
||
size="small"
|
||
text
|
||
type="primary"
|
||
@click="viewCertificates(item.row)"
|
||
>
|
||
查看证书
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<template #index="scope">
|
||
{{ scope.$index + 1 }}
|
||
</template>
|
||
<template #eventType="scope">
|
||
<el-tag :type="scope.row.eventType === 'Online' ? 'primary' : 'success'">
|
||
{{ scope.row.eventType === 'Online' ? '线上' : '线下' }}
|
||
</el-tag>
|
||
</template>
|
||
<template #startTime="scope"> {{ scope.row.startTime }} -- {{ scope.row.endTime }} </template>
|
||
|
||
</ProTable>
|
||
<template #footer>
|
||
<span class="dialog-footer">
|
||
<el-button @click="onCancel">取消</el-button>
|
||
<el-button type="primary" @click="onCancel">确定</el-button>
|
||
</span>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 团队项目证书列表弹窗 -->
|
||
<el-dialog title="团队证书列表" v-model="isShowTeamCertificateList" width="50%" @close="onTeamCertificateListClose">
|
||
<div class="certificate-list-container">
|
||
<div class="certificate-type-section" v-for="(certGroup, index) in certificateList" :key="index">
|
||
<div class="section-title">{{ certGroup.certificateType }}</div>
|
||
<div class="names-list">{{ certGroup.names }}</div>
|
||
<el-button v-if="certGroup.hasCertificate" size="small" text type="primary" @click="openCertificateGroup(certGroup)">查看证书</el-button>
|
||
</div>
|
||
</div>
|
||
<template #footer>
|
||
<span class="dialog-footer">
|
||
<el-button @click="isShowTeamCertificateList = false">关闭</el-button>
|
||
</span>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 审核弹窗 -->
|
||
<el-dialog title="批量审核" v-model="isShowAuditDialog" width="500px">
|
||
<el-form :model="auditForm" label-width="100px">
|
||
<el-form-item label="审核状态">
|
||
<el-radio-group v-model="auditForm.status">
|
||
<el-radio :value="2">审核通过</el-radio>
|
||
<el-radio :value="3">审核拒绝</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="审核备注">
|
||
<el-input v-model="auditForm.remark" type="textarea" :rows="3" placeholder="请输入审核备注"></el-input>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<span class="dialog-footer">
|
||
<el-button @click="isShowAuditDialog = false">取消</el-button>
|
||
<el-button type="primary" @click="onSubmitAudit">确定</el-button>
|
||
</span>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="unit">
|
||
import { defineAsyncComponent, ref, reactive } from 'vue';
|
||
import { ElMessage, ElForm } from 'element-plus';
|
||
import { auths } from '/@/utils/authFunction';
|
||
import ProTable from '/@/components/ProTable/index.vue';
|
||
import { eventrecordPageGet, eventrecordCertificatesGet } from '/@/api/match/eventRecord';
|
||
import { merchantcertificationPageGet, merchantcertificationAuditPost } from '/@/api/match/merchantcertification';
|
||
import PreviewImage from '/@/components/PreviewImage/index.vue';
|
||
|
||
const initParam = ref({
|
||
refresh: true,
|
||
});
|
||
const selectedListIds = ref([]);
|
||
const initParamUser = ref({
|
||
sysUserId: null,
|
||
});
|
||
const statusList = ['info', 'warning', 'success', 'danger'];
|
||
|
||
const getAuditStatusText = (status) => {
|
||
switch (status) {
|
||
case 0:
|
||
return '待提交';
|
||
case 1:
|
||
return '待审核';
|
||
case 2:
|
||
return '审核通过';
|
||
case 3:
|
||
return '审核拒绝';
|
||
case 4:
|
||
return '已取消';
|
||
default:
|
||
return status;
|
||
}
|
||
};
|
||
|
||
const getAuditStatusType = (status) => {
|
||
switch (status) {
|
||
case 0:
|
||
return 'info';
|
||
case 1:
|
||
return 'warning';
|
||
case 2:
|
||
return 'success';
|
||
case 3:
|
||
return 'danger';
|
||
case 4:
|
||
return 'info';
|
||
default:
|
||
return 'info';
|
||
}
|
||
};
|
||
|
||
const columns = ref([
|
||
{ prop: 'ss', type: 'selection' },
|
||
{ prop: 'index', label: '序号', align: 'center' },
|
||
{ prop: 'merchantName', label: '单位名称', search: { el: 'input' } },
|
||
{ prop: 'address', label: '地址' },
|
||
|
||
{ prop: 'contactPhone', label: '联系电话', search: { el: 'input' } },
|
||
{ prop: 'businessLicenseUrl', label: '营业执照', width: '130px' },
|
||
{
|
||
prop: 'auditStatus',
|
||
label: '审核状态',
|
||
search: {
|
||
el: 'select',
|
||
},
|
||
enum: [
|
||
{ label: '全部', value: '' },
|
||
{ label: '审核拒绝', value: 3 },
|
||
{ label: '审核通过', value: 2 },
|
||
{ label: '待审核', value: 1 },
|
||
{ label: '待提交', value: 0 },
|
||
{ label: '已取消', value: 4 },
|
||
],
|
||
},
|
||
{
|
||
prop: 'createdTime',
|
||
label: '生成时间',
|
||
search: {
|
||
el: 'date-picker',
|
||
props: { type: 'daterange' },
|
||
},
|
||
},
|
||
|
||
{ prop: 'action', label: '操作', align: 'center', fixed: 'right', isShow: auths(['user:general:record']) },
|
||
]);
|
||
const tableRef = ref();
|
||
const handleSelectionChange = (val) => {
|
||
console.log(val);
|
||
selectedListIds.value = val.map((item) => item.id);
|
||
};
|
||
|
||
const columnsUser = ref([
|
||
{ prop: 'index', label: '序号', align: 'center' },
|
||
{ prop: 'eventName', label: '赛事名称', search: { el: 'input' } },
|
||
{ prop: 'eventType', label: '赛事类型', width: '100' },
|
||
{
|
||
prop: 'startTime',
|
||
label: '起止时间',
|
||
search: {
|
||
el: 'date-picker',
|
||
props: { type: 'daterange' },
|
||
},
|
||
},
|
||
{
|
||
prop: 'endTime',
|
||
label: '起止时间',
|
||
isShow: false,
|
||
},
|
||
|
||
{ prop: 'action', label: '操作', align: 'center', fixed: 'right' },
|
||
]);
|
||
|
||
const isShowDialog = ref(false);
|
||
const table = ref();
|
||
const certificates = ref([]);
|
||
const isShowTeamCertificateList = ref(false);
|
||
const certificateList = ref([]);
|
||
|
||
const onOpenMenu = (row) => {
|
||
console.log(row);
|
||
|
||
// 参数验证
|
||
if (!row || !row.userId) {
|
||
ElMessage.warning('用户信息无效,无法查看参赛记录');
|
||
return;
|
||
}
|
||
|
||
if (typeof row.userId !== 'number' || row.userId <= 0) {
|
||
ElMessage.warning('用户ID无效,请刷新页面后重试');
|
||
return;
|
||
}
|
||
|
||
// 设置用户ID参数 - 单位用户使用 sysUserId
|
||
initParamUser.value.sysUserId = row.userId;
|
||
isShowDialog.value = true;
|
||
|
||
// 下次打开对话框时强制刷新数据
|
||
initParamUser.value.refresh = Date.now();
|
||
};
|
||
|
||
// 查看证书
|
||
const viewCertificates = (item) => {
|
||
if (item.certificateUrl) {
|
||
openCertificate(item.certificateUrl);
|
||
} else {
|
||
ElMessage.warning('暂无证书');
|
||
}
|
||
};
|
||
|
||
// 加载团队证书列表
|
||
const loadTeamCertificates = async (members) => {
|
||
const certData = {
|
||
athlete: { names: [], memberIds: [], hasCertificate: false, imageUrls: [] },
|
||
coach: { names: [], memberIds: [], hasCertificate: false, imageUrls: [] },
|
||
leader: { names: [], memberIds: [], hasCertificate: false, imageUrls: [] }
|
||
};
|
||
|
||
for (const member of members) {
|
||
try {
|
||
const { succeeded, data } = await eventscoreGetCertificateDetailGet(member.id);
|
||
if (succeeded && data) {
|
||
const imageUrl = data.certificateUrl || data.url || data.Url;
|
||
const name = member.name || data.name || '未知';
|
||
|
||
// 根据后端返回的 personType 判断类型,而不是证书类型
|
||
let typeKey = 'athlete';
|
||
if (member.personType === 2) {
|
||
typeKey = 'coach';
|
||
} else if (member.personType === 3) {
|
||
typeKey = 'leader';
|
||
}
|
||
|
||
if (imageUrl) {
|
||
certData[typeKey].hasCertificate = true;
|
||
certData[typeKey].imageUrls.push(imageUrl);
|
||
}
|
||
certData[typeKey].names.push(name);
|
||
certData[typeKey].memberIds.push(member.id);
|
||
} else {
|
||
// 即使没有证书,也要添加姓名到列表
|
||
const name = member.name || '未知';
|
||
let typeKey = 'athlete';
|
||
if (member.personType === 2) {
|
||
typeKey = 'coach';
|
||
} else if (member.personType === 3) {
|
||
typeKey = 'leader';
|
||
}
|
||
certData[typeKey].names.push(name);
|
||
certData[typeKey].memberIds.push(member.id);
|
||
}
|
||
} catch (error) {
|
||
console.error('获取证书失败', error);
|
||
// 即使获取证书失败,也添加姓名到列表中
|
||
const name = member.name || '未知';
|
||
let typeKey = 'athlete';
|
||
if (member.personType === 2) {
|
||
typeKey = 'coach';
|
||
} else if (member.personType === 3) {
|
||
typeKey = 'leader';
|
||
}
|
||
certData[typeKey].names.push(name);
|
||
certData[typeKey].memberIds.push(member.id);
|
||
}
|
||
}
|
||
|
||
// 构建显示列表
|
||
certificateList.value = [];
|
||
if (certData.athlete.names.length > 0) {
|
||
certificateList.value.push({
|
||
certificateType: '运动员证书',
|
||
names: certData.athlete.names.join('、'),
|
||
hasCertificate: certData.athlete.hasCertificate,
|
||
imageUrls: certData.athlete.imageUrls,
|
||
memberIds: certData.athlete.memberIds
|
||
});
|
||
}
|
||
if (certData.coach.names.length > 0) {
|
||
certificateList.value.push({
|
||
certificateType: '教练证书',
|
||
names: certData.coach.names.join('、'),
|
||
hasCertificate: certData.coach.hasCertificate,
|
||
imageUrls: certData.coach.imageUrls,
|
||
memberIds: certData.coach.memberIds
|
||
});
|
||
}
|
||
if (certData.leader.names.length > 0) {
|
||
certificateList.value.push({
|
||
certificateType: '领队证书',
|
||
names: certData.leader.names.join('、'),
|
||
hasCertificate: certData.leader.hasCertificate,
|
||
imageUrls: certData.leader.imageUrls,
|
||
memberIds: certData.leader.memberIds
|
||
});
|
||
}
|
||
|
||
if (certificateList.value.length === 0) {
|
||
ElMessage.warning('暂无证书');
|
||
} else {
|
||
isShowTeamCertificateList.value = true;
|
||
}
|
||
};
|
||
|
||
// 打开证书组
|
||
const openCertificateGroup = (certGroup) => {
|
||
if (certGroup.imageUrls && certGroup.imageUrls.length > 0) {
|
||
// 打开第一个证书,或者如果有多个,可以考虑弹窗选择
|
||
for (const url of certGroup.imageUrls) {
|
||
if (url) {
|
||
openCertificate(url);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
// 打开证书图片
|
||
const openCertificate = (url) => {
|
||
if (url) {
|
||
window.open(url, '_blank');
|
||
} else {
|
||
ElMessage.warning('证书链接无效');
|
||
}
|
||
};
|
||
|
||
// 关闭团队证书列表弹窗
|
||
const onTeamCertificateListClose = () => {
|
||
certificateList.value = [];
|
||
};
|
||
|
||
// 审核弹窗相关
|
||
const isShowAuditDialog = ref(false);
|
||
const auditForm = reactive({
|
||
status: 2, // 默认审核通过
|
||
remark: '',
|
||
});
|
||
|
||
const showAuditDialog = () => {
|
||
if (selectedListIds.value.length === 0) {
|
||
ElMessage.warning('请先选择要审核的记录');
|
||
return;
|
||
}
|
||
auditForm.status = 2;
|
||
auditForm.remark = '';
|
||
isShowAuditDialog.value = true;
|
||
};
|
||
|
||
const onSubmitAudit = async () => {
|
||
console.log(selectedListIds.value);
|
||
const { succeeded, data } = await merchantcertificationAuditPost({
|
||
ids: selectedListIds.value,
|
||
status: auditForm.status,
|
||
remark: auditForm.remark,
|
||
});
|
||
if (succeeded) {
|
||
ElMessage.success('操作成功');
|
||
isShowAuditDialog.value = false;
|
||
tableRef.value.clearSelection();
|
||
tableRef.value.getTableList();
|
||
} else {
|
||
ElMessage.error(data);
|
||
}
|
||
};
|
||
|
||
// 对话框关闭时清理
|
||
const onDialogClose = () => {
|
||
// 清理参数,避免下次查询时使用旧参数
|
||
initParamUser.value = {
|
||
sysUserId: null,
|
||
refresh: Date.now(),
|
||
};
|
||
};
|
||
|
||
// 取消
|
||
const onCancel = () => {
|
||
isShowDialog.value = false;
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.dialog-footer {
|
||
margin-top: 80px;
|
||
}
|
||
|
||
.certificate-list-container {
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
|
||
.certificate-type-section {
|
||
margin-bottom: 24px;
|
||
padding: 16px;
|
||
background-color: #f5f7fa;
|
||
border-radius: 8px;
|
||
|
||
.section-title {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
margin-bottom: 12px;
|
||
color: #303133;
|
||
}
|
||
|
||
.names-list {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
margin-bottom: 12px;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
}
|
||
</style>
|