scoreDetail.vue 7.99 KB
<template>
	<view>
		<scroll-view scroll-y class="detail-content">
			<!-- 基本信息 -->
			<view class="card">
				<view class="card-header">
					<view class="header-left">考试信息</view>
				</view>
				<view class="card-body">
					<view class="info-row">
						<text class="label">考试名称</text>
						<text class="value">{{ form.name || '--' }}</text>
					</view>
					<view class="info-row">
						<text class="label">考试日期</text>
						<text class="value">{{ form.startTime ? form.startTime.substring(0, 16) : '--' }}{{ form.endTime ? form.endTime.substring(0, 16) : '--' }}</text>
					</view>
					<view class="info-row">
						<text class="label">考试地点</text>
						<text class="value">{{ form.address || '--' }}</text>
					</view>
					<view class="info-row">
						<text class="label">申请单位</text>
						<text class="value">{{ form.memberName || '--' }}</text>
					</view>
					<view class="info-row">
						<text class="label">考官</text>
						<text class="value">{{ form.examinerNames || '--' }}</text>
					</view>
				</view>
			</view>

			<!-- 考生信息 -->
			<view class="card">
				<view class="card-header">
					<view class="header-left">考生信息</view>
					<view class="header-right">{{ list.length }}</view>
				</view>
				<view v-if="loading" class="state-tip">加载中...</view>
				<view v-else-if="list.length === 0" class="state-tip">暂无考生信息</view>
				<view class="student-list" v-else>
					<view class="student-card" v-for="n in list" :key="n.id">
						<view class="student-top">
							<view class="student-name">
								<text class="name">{{ n.realName || '--' }}</text>
							</view>
							<view class="score-tag" :class="n.isPass == 1 ? 'pass' : 'fail'">
								{{ n.isPass == 1 ? '通过' : '未通过' }}
							</view>
						</view>
						<view class="student-info">
							<view class="info-col">
								<text class="info-text">{{ n.idcCode || '--' }}</text>
							</view>
							<view class="info-col">
								<text class="info-text">{{ !n.levelOld || n.levelOld == 0 ? '十' : szToHz(n.levelOld) }}</text>
							</view>
							<view class="info-col">
								<text class="info-text">{{ n.levelNew ? szToHz(n.levelNew) + '级' : '--' }}</text>
							</view>
							<view class="info-col">
								<text class="info-text text-red">¥{{ (Number(n.examFee) || 0).toFixed(2) }}</text>
							</view>
						</view>
					</view>
				</view>
			</view>

			<!-- 审核记录 -->
			<view class="h3-padding">审核记录</view>
			<view class="wBox">
				<view v-if="loadingAudit" class="state-tip">加载中...</view>
				<view v-else-if="recordList.length === 0" class="state-tip">暂无审核记录</view>
				<view class="stepItem" v-else v-for="(n, index) in recordList" :key="index">
					<view class="time">{{ n.auditTime || '待审批' }}</view>
					<view class="content">
						<view class="status">
							<text v-if="n.auditResult==1" class="text-success">审核通过</text>
							<text v-else-if="n.auditResult==0" class="text-danger">审核拒绝</text>
							<text v-else class="text-primary">审核中</text>
						</view>
						<view class="deptName">{{ n.auditDeptName || '--' }}</view>
						<view v-if="n.auditMsg">备注:{{ n.auditMsg }}</view>
					</view>
				</view>
			</view>
		</scroll-view>
	</view>
</template>

<script setup>
	import * as api from '@/common/api.js'
	import { szToHz } from '@/common/utils.js'
	import { ref } from 'vue'
	import { onLoad } from '@dcloudio/uni-app'

	const form = ref({})
	const list = ref([])
	const recordList = ref([])
	const loading = ref(false)
	const loadingAudit = ref(false)

	const selectForm = ref({ pageNum: 1, pageSize: 100 })
	let examId = ''
	let examType = ref('2')

	onLoad((option) => {
		examId = option.examId
		if (option.type) examType.value = option.type
		initData()
	})

	function initData() {
		uni.showLoading({ title: '加载中' })
		getForm()
		getList()
		getRecordList()
	}

	function getForm() {
		api.getExamInfo(examId).then(res => {
			uni.hideLoading()
			if (res.code === 200 || res.code === 0) {
				form.value = res.data || {}
			}
		}).catch(() => { uni.hideLoading() })
	}

	function getList() {
		loading.value = true
		selectForm.value.examId = examId
		api.getStudentList(selectForm.value).then(res => {
			if (res && res.rows) {
				list.value = res.rows
			} else {
				list.value = []
			}
		}).catch(() => { list.value = [] }).finally(() => { loading.value = false })
	}

	function getRecordList() {
		loadingAudit.value = true
		const typeMap = { '2': 1, '3': 2, '4': 4 }
		const logType = typeMap[examType.value] || 1
		api.getLogs(examId, logType).then(res => {
			if (res && res.data) {
				recordList.value = Array.isArray(res.data) ? res.data : (res.data.levelSteps || [])
			} else {
				recordList.value = []
			}
		}).catch(() => { recordList.value = [] }).finally(() => { loadingAudit.value = false })
	}
</script>

<style scoped lang="scss">
$primary-color: #e8341d;
$success-color: #52c41a;
$danger-color: #e8341c;
$bg-color: #f5f7fa;
$card-bg: #ffffff;
$text-primary: #333;
$text-placeholder: #999;
$border-color: #eee;
$content-gap: 24rpx;

.detail-content {
	padding: 20rpx;
	background: $bg-color;
	min-height: 100vh;
	box-sizing: border-box;
}

.h3-padding {
	padding: 30rpx 30rpx 0;
	font-size: 30rpx;
	font-weight: 600;
	color: #333;
}

.wBox {
	width: 700rpx;
	padding: 30rpx;
	margin: 20rpx auto 0;
	background: #FFFFFF;
	box-shadow: 0rpx 12rpx 116rpx 0rpx rgba(196, 203, 214, 0.1);
	border-radius: 15rpx;
}

.stepItem {
	display: flex;
	padding: 16rpx 0;
	border-bottom: 1rpx dashed #eee;

	&:last-child {
		border-bottom: none;
	}

	.time {
		width: 160rpx;
		font-size: 22rpx;
		color: #999;
		flex-shrink: 0;
		padding-top: 4rpx;
	}

	.content {
		flex: 1;

		.status {
			font-size: 28rpx;
			font-weight: 600;
			margin-bottom: 6rpx;
		}

		.deptName {
			font-size: 26rpx;
			color: #666;
		}

		view {
			font-size: 24rpx;
			color: #999;
			margin-top: 4rpx;
		}
	}
}

.card {
	background: $card-bg;
	border-radius: 16rpx;
	margin-bottom: 20rpx;
	overflow: hidden;
	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}

.card-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 24rpx $content-gap 20rpx;
	border-bottom: 1rpx solid $border-color;

	.header-left {
		font-size: 30rpx;
		font-weight: 600;
		color: $text-primary;
	}

	.header-right {
		font-size: 26rpx;
		color: $primary-color;
		font-weight: 500;
	}
}

.card-body {
	padding: 8rpx $content-gap 24rpx;
}

.info-row {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 18rpx 0;
	border-bottom: 1rpx solid #f5f5f5;

	&:last-child {
		border-bottom: none;
	}

	.label {
		font-size: 28rpx;
		color: $text-placeholder;
		flex-shrink: 0;
		width: 170rpx;
	}

	.value {
		font-size: 28rpx;
		color: $text-primary;
		text-align: right;
		flex: 1;
		word-break: break-all;
		padding-left: 10rpx;
	}
}

.state-tip {
	text-align: center;
	padding: 60rpx 0;
	font-size: 26rpx;
	color: $text-placeholder;
}

.student-list {
	padding: 0 $content-gap 24rpx;
}

.student-card {
	border-radius: 12rpx;
	padding: 24rpx;
	margin-top: 16rpx;
	border: 1rpx solid $border-color;

	.student-top {
		display: flex;
		justify-content: space-between;
		align-items: center;
		margin-bottom: 20rpx;

		.student-name .name {
			font-size: 32rpx;
			font-weight: 600;
			color: $text-primary;
		}
	}

	.student-info {
		display: flex;
		justify-content: space-between;
		align-items: center;
		gap: 12rpx;

		.info-col {
			flex: 1;
			text-align: center;

			.info-text {
				font-size: 28rpx;
				color: $text-primary;
				font-weight: 500;
				line-height: 1.4;
			}
		}
	}
}

.score-tag {
	font-size: 24rpx;
	padding: 6rpx 16rpx;
	border-radius: 20rpx;
	flex-shrink: 0;
	font-weight: 500;

	&.pass {
		background: rgba($success-color, 0.1);
		color: $success-color;
		border: 1rpx solid rgba($success-color, 0.2);
	}

	&.fail {
		background: rgba($danger-color, 0.1);
		color: $danger-color;
		border: 1rpx solid rgba($danger-color, 0.2);
	}
}

.text-red {
	color: $danger-color !important;
}
</style>