wuzhen update
This commit is contained in:
1 parent
715673dce9
commit
b8da4c17bf
7 files changed
+41
-1737
No files matched your search
@@ -1,303 +0,0 @@
|
||||
<template>
|
||||
<div class="all-applications">
|
||||
<!-- 搜索表单 -->
|
||||
<el-card class="search-card" shadow="never">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" class="search-form">
|
||||
<el-form-item label="岗位名称" prop="positionName">
|
||||
<el-input v-model="queryParams.positionName" placeholder="请输入岗位名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学生姓名" prop="studentName">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择申请状态" clearable style="width: 200px">
|
||||
<el-option v-for="option in statusOptions" :key="option.value" :label="option.label" :value="option.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请日期" prop="applicationDate">
|
||||
<el-date-picker
|
||||
v-model="queryParams.applicationDate"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-card class="table-card" shadow="never">
|
||||
<div class="table-header">
|
||||
<div class="table-title">
|
||||
<el-icon><List /></el-icon>
|
||||
所有申请列表
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="applicationList" row-key="id" stripe>
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="岗位名称" prop="positionName" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="学生信息" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<div class="student-info">
|
||||
<div class="name">{{ row.studentName }}</div>
|
||||
<div class="detail">{{ row.studentNo }} | {{ row.className }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="专业年级" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.majorName }}</div>
|
||||
<div class="text-gray">{{ row.grade }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="联系方式" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.contactPhone }}</div>
|
||||
<div class="text-gray">{{ row.email }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请理由" prop="applicationReason" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="申请状态" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getStatusTagType(row.status)" effect="plain" size="small">
|
||||
{{ getStatusText(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请时间" prop="applicationDate" width="110" align="center" />
|
||||
<el-table-column label="审核时间" width="130" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.collegeReviewTime">{{ row.collegeReviewTime }}</span>
|
||||
<span v-else class="text-gray">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核意见" min-width="150" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.collegeReviewComment">{{ row.collegeReviewComment }}</span>
|
||||
<span v-else class="text-gray">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" size="small" icon="View" @click="handleDetail(row)"> 详情 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<ApplicationDetail v-model:visible="detailDialog.visible" :application-id="detailDialog.applicationId" @refresh="getList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="AllApplications">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { Search, List, View } from '@element-plus/icons-vue';
|
||||
import {
|
||||
listCollegeApplications,
|
||||
getApplicationOptions,
|
||||
type WorkStudyApplicationVO,
|
||||
type WorkStudyApplicationQuery,
|
||||
type OptionsVO
|
||||
} from '@/api/jcbt/workStudyApplicationCollegeReview';
|
||||
import ApplicationDetail from './ApplicationDetail.vue';
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const applicationList = ref<WorkStudyApplicationVO[]>([]);
|
||||
const total = ref(0);
|
||||
const statusOptions = ref<Array<{ label: string; value: string }>>([]);
|
||||
const queryRef = ref();
|
||||
|
||||
// 查询参数
|
||||
const queryParams = ref<WorkStudyApplicationQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
positionName: '',
|
||||
studentName: '',
|
||||
studentNo: '',
|
||||
status: '',
|
||||
applicationDate: ''
|
||||
});
|
||||
|
||||
// 详情对话框数据
|
||||
const detailDialog = reactive({
|
||||
visible: false,
|
||||
applicationId: ''
|
||||
});
|
||||
|
||||
// 获取所有申请列表
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await listCollegeApplications(queryParams.value);
|
||||
applicationList.value = data.rows || [];
|
||||
total.value = data.total || 0;
|
||||
} catch (error) {
|
||||
console.error('获取申请列表失败:', error);
|
||||
ElMessage.error('获取数据失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取选项数据
|
||||
const getOptions = async () => {
|
||||
try {
|
||||
const { data } = await getApplicationOptions();
|
||||
statusOptions.value = data.statusOptions || [];
|
||||
} catch (error) {
|
||||
console.error('获取选项数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
queryRef.value.resetFields();
|
||||
queryParams.value = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
positionName: '',
|
||||
studentName: '',
|
||||
studentNo: '',
|
||||
status: '',
|
||||
applicationDate: ''
|
||||
};
|
||||
getList();
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleDetail = (row: WorkStudyApplicationVO) => {
|
||||
detailDialog.visible = true;
|
||||
detailDialog.applicationId = row.id as string;
|
||||
};
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'SUBMITTED': 'info',
|
||||
'COLLEGE_APPROVED': 'success',
|
||||
'COLLEGE_REJECTED': 'danger',
|
||||
'FINAL_APPROVED': 'success',
|
||||
'FINAL_REJECTED': 'danger',
|
||||
'CANCELLED': 'info'
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
const textMap: Record<string, string> = {
|
||||
'SUBMITTED': '已提交',
|
||||
'COLLEGE_APPROVED': '学院已通过',
|
||||
'COLLEGE_REJECTED': '学院已拒绝',
|
||||
'FINAL_APPROVED': '最终通过',
|
||||
'FINAL_REJECTED': '最终拒绝',
|
||||
'CANCELLED': '已取消'
|
||||
};
|
||||
return textMap[status] || status;
|
||||
};
|
||||
|
||||
// 暴露方法给父组件调用
|
||||
defineExpose({
|
||||
getList
|
||||
});
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getOptions();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.all-applications {
|
||||
.search-card {
|
||||
margin-bottom: 16px;
|
||||
border: none;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-form {
|
||||
.el-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.table-card {
|
||||
border: none;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px 20px 16px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
.table-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
|
||||
.el-icon {
|
||||
margin-right: 8px;
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-table {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
.name {
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
.text-gray {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="申请详情" width="800px" append-to-body :close-on-click-modal="false" @closed="handleClose">
|
||||
<div v-loading="loading" class="application-detail">
|
||||
<template v-if="applicationData">
|
||||
<!-- 基本信息 -->
|
||||
<div class="detail-section">
|
||||
<div class="section-title">
|
||||
<el-icon><User /></el-icon>
|
||||
基本信息
|
||||
</div>
|
||||
<div class="section-content">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>学生姓名:</label>
|
||||
<span>{{ applicationData.studentName }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>学号:</label>
|
||||
<span>{{ applicationData.studentNo }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>学院:</label>
|
||||
<span>{{ applicationData.collegeName }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>专业:</label>
|
||||
<span>{{ applicationData.majorName }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>年级:</label>
|
||||
<span>{{ applicationData.grade }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>班级:</label>
|
||||
<span>{{ applicationData.className }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>联系电话:</label>
|
||||
<span>{{ applicationData.contactPhone }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>邮箱:</label>
|
||||
<span>{{ applicationData.email }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 岗位信息 -->
|
||||
<div class="detail-section">
|
||||
<div class="section-title">
|
||||
<el-icon><Briefcase /></el-icon>
|
||||
岗位信息
|
||||
</div>
|
||||
<div class="section-content">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<div class="info-item">
|
||||
<label>岗位名称:</label>
|
||||
<span>{{ applicationData.positionName }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 申请信息 -->
|
||||
<div class="detail-section">
|
||||
<div class="section-title">
|
||||
<el-icon><Document /></el-icon>
|
||||
申请信息
|
||||
</div>
|
||||
<div class="section-content">
|
||||
<div class="info-item full-width">
|
||||
<label>工作经验:</label>
|
||||
<div class="text-content">{{ applicationData.workExperience || '无' }}</div>
|
||||
</div>
|
||||
<div class="info-item full-width">
|
||||
<label>申请理由:</label>
|
||||
<div class="text-content">{{ applicationData.applicationReason || '无' }}</div>
|
||||
</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>申请状态:</label>
|
||||
<el-tag :type="getStatusTagType(applicationData.status)" effect="plain">
|
||||
{{ getStatusText(applicationData.status) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="info-item">
|
||||
<label>申请时间:</label>
|
||||
<span>{{ applicationData.applicationDate }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 审核记录 -->
|
||||
<div class="detail-section">
|
||||
<div class="section-title">
|
||||
<el-icon><Clock /></el-icon>
|
||||
审核记录
|
||||
</div>
|
||||
<div class="section-content">
|
||||
<!-- 学院审核记录 -->
|
||||
<div v-if="applicationData.collegeReviewTime" class="review-record">
|
||||
<div class="review-header">
|
||||
<div class="review-type">学院审核</div>
|
||||
<div class="review-time">{{ applicationData.collegeReviewTime }}</div>
|
||||
</div>
|
||||
<div class="review-content">
|
||||
<div class="reviewer">审核人:{{ applicationData.collegeReviewByName || '未知' }}</div>
|
||||
<div class="review-comment">
|
||||
<label>审核意见:</label>
|
||||
<span>{{ applicationData.collegeReviewComment || '无' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最终审核记录 -->
|
||||
<div v-if="applicationData.finalReviewTime" class="review-record">
|
||||
<div class="review-header">
|
||||
<div class="review-type">最终审核</div>
|
||||
<div class="review-time">{{ applicationData.finalReviewTime }}</div>
|
||||
</div>
|
||||
<div class="review-content">
|
||||
<div class="reviewer">审核人:{{ applicationData.finalReviewByName || '未知' }}</div>
|
||||
<div class="review-comment">
|
||||
<label>审核意见:</label>
|
||||
<span>{{ applicationData.finalReviewComment || '无' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无审核记录 -->
|
||||
<div v-if="!applicationData.collegeReviewTime && !applicationData.finalReviewTime" class="no-review">暂无审核记录</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose">关闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ApplicationDetail">
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { User, Document, Clock, Briefcase } from '@element-plus/icons-vue';
|
||||
import { getCollegeApplication, type WorkStudyApplicationVO } from '@/api/jcbt/workStudyApplicationCollegeReview';
|
||||
|
||||
// 定义属性
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
applicationId: string | number;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits<{
|
||||
'update:visible': [value: boolean];
|
||||
'refresh': [];
|
||||
}>();
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const applicationData = ref<WorkStudyApplicationVO>();
|
||||
|
||||
// 计算属性
|
||||
const dialogVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: (value) => emit('update:visible', value)
|
||||
});
|
||||
|
||||
// 获取申请详情
|
||||
const getApplicationDetail = async () => {
|
||||
if (!props.applicationId) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await getCollegeApplication(props.applicationId);
|
||||
applicationData.value = data;
|
||||
} catch (error) {
|
||||
console.error('获取申请详情失败:', error);
|
||||
ElMessage.error('获取详情失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'SUBMITTED': 'info',
|
||||
'COLLEGE_APPROVED': 'success',
|
||||
'COLLEGE_REJECTED': 'danger',
|
||||
'FINAL_APPROVED': 'success',
|
||||
'FINAL_REJECTED': 'danger',
|
||||
'CANCELLED': 'info'
|
||||
};
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
const textMap: Record<string, string> = {
|
||||
'SUBMITTED': '已提交',
|
||||
'COLLEGE_APPROVED': '学院已通过',
|
||||
'COLLEGE_REJECTED': '学院已拒绝',
|
||||
'FINAL_APPROVED': '最终通过',
|
||||
'FINAL_REJECTED': '最终拒绝',
|
||||
'CANCELLED': '已取消'
|
||||
};
|
||||
return textMap[status] || status;
|
||||
};
|
||||
|
||||
// 关闭对话框
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
applicationData.value = undefined;
|
||||
};
|
||||
|
||||
// 监听对话框显示状态
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible && props.applicationId) {
|
||||
getApplicationDetail();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.application-detail {
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
margin-bottom: 24px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 2px solid #e4e7ed;
|
||||
|
||||
.el-icon {
|
||||
margin-right: 8px;
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.section-content {
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 12px;
|
||||
|
||||
&.full-width {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
label {
|
||||
min-width: 80px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.text-content {
|
||||
margin-top: 4px;
|
||||
line-height: 1.6;
|
||||
color: #303133;
|
||||
padding: 8px 12px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #409eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.review-record {
|
||||
margin-bottom: 16px;
|
||||
padding: 16px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #409eff;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.review-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.review-type {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.review-time {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.review-content {
|
||||
.reviewer {
|
||||
color: #606266;
|
||||
font-size: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.review-comment {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
label {
|
||||
min-width: 70px;
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #303133;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-review {
|
||||
text-align: center;
|
||||
color: #c0c4cc;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -1,252 +0,0 @@
|
||||
<template>
|
||||
<el-card class="debug-panel" shadow="never">
|
||||
<template #header>
|
||||
<div class="debug-header">
|
||||
<el-icon><Tools /></el-icon>
|
||||
<span>数据一致性调试面板</span>
|
||||
<el-button type="primary" size="small" @click="runDebug" :loading="debugging">
|
||||
{{ debugging ? '检查中...' : '开始检查' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="debugResult" class="debug-content">
|
||||
<el-row :gutter="20">
|
||||
<!-- 统计API结果 -->
|
||||
<el-col :span="12">
|
||||
<div class="debug-section">
|
||||
<h4>📊 统计API结果</h4>
|
||||
<el-descriptions :column="1" size="small" border>
|
||||
<el-descriptions-item label="待审核数量">
|
||||
<el-tag :type="debugResult.statistics.pendingCount > 0 ? 'warning' : 'info'">
|
||||
{{ debugResult.statistics.pendingCount }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="已通过数量">
|
||||
<el-tag type="success">{{ debugResult.statistics.approvedCount }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="已拒绝数量">
|
||||
<el-tag type="danger">{{ debugResult.statistics.rejectedCount }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最终通过数量">
|
||||
<el-tag type="primary">{{ debugResult.statistics.finalApprovedCount }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="总数量">
|
||||
<el-tag>{{ debugResult.statistics.totalCount }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 列表API结果 -->
|
||||
<el-col :span="12">
|
||||
<div class="debug-section">
|
||||
<h4>📋 列表API结果</h4>
|
||||
<el-descriptions :column="1" size="small" border>
|
||||
<el-descriptions-item label="待审核列表总数">
|
||||
<el-tag :type="debugResult.pendingList.total > 0 ? 'warning' : 'info'">
|
||||
{{ debugResult.pendingList.total }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="所有申请总数">
|
||||
<el-tag>{{ debugResult.allList.total }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="当前页记录数">
|
||||
<el-tag type="info">{{ debugResult.pendingList.rows?.length || 0 }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 一致性检查结果 -->
|
||||
<div class="consistency-check">
|
||||
<h4>🔍 一致性检查结果</h4>
|
||||
<el-alert
|
||||
:type="debugResult.isConsistent ? 'success' : 'error'"
|
||||
:title="debugResult.isConsistent ? '数据一致' : '数据不一致'"
|
||||
:description="debugResult.message"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
|
||||
<div v-if="!debugResult.isConsistent" class="inconsistency-details">
|
||||
<el-tag type="danger">
|
||||
统计API待审核: {{ debugResult.statistics.pendingCount }}
|
||||
</el-tag>
|
||||
<span class="vs">VS</span>
|
||||
<el-tag type="warning">
|
||||
列表API待审核: {{ debugResult.pendingList.total }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详细数据 -->
|
||||
<el-collapse class="debug-details">
|
||||
<el-collapse-item title="📊 统计API原始数据" name="stats">
|
||||
<pre>{{ JSON.stringify(debugResult.statistics, null, 2) }}</pre>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="📋 待审核列表API原始数据" name="pending">
|
||||
<pre>{{ JSON.stringify(debugResult.pendingList, null, 2) }}</pre>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="📄 所有申请列表API原始数据" name="all">
|
||||
<pre>{{ JSON.stringify(debugResult.allList, null, 2) }}</pre>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
|
||||
<div v-else class="no-debug">
|
||||
<el-empty description="点击"开始检查"按钮来诊断数据一致性问题" />
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DebugPanel">
|
||||
import { ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { Tools } from '@element-plus/icons-vue';
|
||||
import {
|
||||
getCollegeStatistics,
|
||||
listPendingApplications,
|
||||
listCollegeApplications,
|
||||
type StatisticsVO,
|
||||
type WorkStudyApplicationVO
|
||||
} from '@/api/jcbt/workStudyApplicationCollegeReview';
|
||||
|
||||
// 响应式数据
|
||||
const debugging = ref(false);
|
||||
const debugResult = ref<any>(null);
|
||||
|
||||
// 运行调试检查
|
||||
const runDebug = async () => {
|
||||
debugging.value = true;
|
||||
debugResult.value = null;
|
||||
|
||||
try {
|
||||
console.log('🔍 开始数据一致性调试...');
|
||||
|
||||
// 1. 获取统计数据
|
||||
const statsResponse = await getCollegeStatistics();
|
||||
const statistics = statsResponse.data;
|
||||
|
||||
// 2. 获取待审核列表数据
|
||||
const pendingResponse = await listPendingApplications({
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
const pendingList = pendingResponse.data;
|
||||
|
||||
// 3. 获取所有申请列表数据
|
||||
const allResponse = await listCollegeApplications({
|
||||
pageNum: 1,
|
||||
pageSize: 1
|
||||
});
|
||||
const allList = allResponse.data;
|
||||
|
||||
// 4. 检查一致性
|
||||
const isConsistent = statistics.pendingCount === pendingList.total;
|
||||
const message = isConsistent
|
||||
? '统计数据与列表数据一致,没有发现问题。'
|
||||
: `数据不一致:统计API显示${statistics.pendingCount}条待审核记录,但列表API显示${pendingList.total}条记录。`;
|
||||
|
||||
debugResult.value = {
|
||||
statistics,
|
||||
pendingList,
|
||||
allList,
|
||||
isConsistent,
|
||||
message,
|
||||
timestamp: new Date().toLocaleString()
|
||||
};
|
||||
|
||||
console.log('✅ 调试检查完成:', debugResult.value);
|
||||
|
||||
if (!isConsistent) {
|
||||
ElMessage.warning('发现数据不一致问题,请查看详细信息');
|
||||
} else {
|
||||
ElMessage.success('数据一致性检查通过');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 调试检查失败:', error);
|
||||
ElMessage.error('调试检查失败,请查看控制台日志');
|
||||
} finally {
|
||||
debugging.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.debug-panel {
|
||||
margin-bottom: 20px;
|
||||
border: 2px dashed #e6a23c;
|
||||
|
||||
.debug-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.el-icon {
|
||||
margin-right: 8px;
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
font-weight: 600;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
|
||||
.debug-content {
|
||||
.debug-section {
|
||||
margin-bottom: 20px;
|
||||
|
||||
h4 {
|
||||
margin: 0 0 12px 0;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.consistency-check {
|
||||
margin: 20px 0;
|
||||
|
||||
h4 {
|
||||
margin: 0 0 12px 0;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.inconsistency-details {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.vs {
|
||||
color: #f56c6c;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.debug-details {
|
||||
margin-top: 20px;
|
||||
|
||||
pre {
|
||||
background: #f5f7fa;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-debug {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
-401
@@ -1,401 +0,0 @@
|
||||
<template>
|
||||
<div class="pending-applications">
|
||||
<!-- 搜索表单 -->
|
||||
<el-card class="search-card" shadow="never">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" class="search-form">
|
||||
<el-form-item label="岗位名称" prop="positionName">
|
||||
<el-input v-model="queryParams.positionName" placeholder="请输入岗位名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学生姓名" prop="studentName">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请日期" prop="applicationDate">
|
||||
<el-date-picker
|
||||
v-model="queryParams.applicationDate"
|
||||
type="date"
|
||||
placeholder="选择申请日期"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 操作按钮和数据表格 -->
|
||||
<el-card class="table-card" shadow="never">
|
||||
<!-- 操作按钮 -->
|
||||
<div class="table-header">
|
||||
<div class="table-title">
|
||||
<el-icon><Clock /></el-icon>
|
||||
待审核申请列表
|
||||
</div>
|
||||
<div class="table-actions">
|
||||
<el-button type="success" plain icon="Check" :disabled="multiple" @click="handleBatchReview('APPROVED')"> 批量通过 </el-button>
|
||||
<el-button type="danger" plain icon="Close" :disabled="multiple" @click="handleBatchReview('REJECTED')"> 批量拒绝 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-table v-loading="loading" :data="applicationList" @selection-change="handleSelectionChange" row-key="id" stripe>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="岗位名称" prop="positionName" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="学生信息" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<div class="student-info">
|
||||
<div class="name">{{ row.studentName }}</div>
|
||||
<div class="detail">{{ row.studentNo }} | {{ row.className }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="专业年级" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.majorName }}</div>
|
||||
<div class="text-gray">{{ row.grade }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="联系方式" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.contactPhone }}</div>
|
||||
<div class="text-gray">{{ row.email }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请理由" prop="applicationReason" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="申请时间" prop="applicationDate" width="110" align="center" />
|
||||
<el-table-column label="操作" fixed="right" width="200" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" size="small" icon="View" @click="handleDetail(row)"> 详情 </el-button>
|
||||
<el-button type="success" size="small" icon="Check" @click="handleReview(row, 'APPROVED')"> 通过 </el-button>
|
||||
<el-button type="danger" size="small" icon="Close" @click="handleReview(row, 'REJECTED')"> 拒绝 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<!-- 审核对话框 -->
|
||||
<el-dialog v-model="reviewDialog.visible" :title="reviewDialog.title" width="600px" append-to-body :close-on-click-modal="false">
|
||||
<el-form ref="reviewFormRef" :model="reviewDialog.form" :rules="reviewRules" label-width="100px">
|
||||
<el-form-item label="审核结果" prop="reviewResult">
|
||||
<el-radio-group v-model="reviewDialog.form.reviewResult">
|
||||
<el-radio value="APPROVED">
|
||||
<el-icon color="#67c23a"><Check /></el-icon>
|
||||
通过
|
||||
</el-radio>
|
||||
<el-radio value="REJECTED">
|
||||
<el-icon color="#f56c6c"><Close /></el-icon>
|
||||
拒绝
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核意见" prop="reviewComment">
|
||||
<el-input v-model="reviewDialog.form.reviewComment" type="textarea" :rows="4" placeholder="请输入审核意见(必填)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="reviewDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="reviewDialog.submitting" @click="submitReview"> 确认审核 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<ApplicationDetail v-model:visible="detailDialog.visible" :application-id="detailDialog.applicationId" @refresh="getList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="PendingApplications">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Search, Clock, Check, Close, View } from '@element-plus/icons-vue';
|
||||
import {
|
||||
listPendingApplications,
|
||||
reviewApplication,
|
||||
batchReviewApplications,
|
||||
type WorkStudyApplicationVO,
|
||||
type WorkStudyApplicationQuery,
|
||||
type ReviewForm
|
||||
} from '@/api/jcbt/workStudyApplicationCollegeReview';
|
||||
import ApplicationDetail from './ApplicationDetail.vue';
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits<{
|
||||
'refresh-statistics': [];
|
||||
}>();
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const applicationList = ref<WorkStudyApplicationVO[]>([]);
|
||||
const total = ref(0);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const multiple = ref(true);
|
||||
const queryRef = ref();
|
||||
const reviewFormRef = ref();
|
||||
|
||||
// 查询参数
|
||||
const queryParams = ref<WorkStudyApplicationQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
positionName: '',
|
||||
studentName: '',
|
||||
studentNo: '',
|
||||
applicationDate: ''
|
||||
});
|
||||
|
||||
// 审核对话框数据
|
||||
const reviewDialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
type: 'single', // single | batch
|
||||
submitting: false,
|
||||
form: {
|
||||
applicationId: '',
|
||||
reviewResult: '',
|
||||
reviewComment: ''
|
||||
}
|
||||
});
|
||||
|
||||
// 详情对话框数据
|
||||
const detailDialog = reactive({
|
||||
visible: false,
|
||||
applicationId: ''
|
||||
});
|
||||
|
||||
// 审核表单验证规则
|
||||
const reviewRules = {
|
||||
reviewResult: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
|
||||
reviewComment: [{ required: true, message: '请输入审核意见', trigger: 'blur' }]
|
||||
};
|
||||
|
||||
// 获取待审核申请列表
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
console.log('🔍 开始获取待审核申请列表...');
|
||||
console.log('📝 查询参数:', queryParams.value);
|
||||
|
||||
const { data } = await listPendingApplications(queryParams.value);
|
||||
console.log('📋 列表API返回数据:', data);
|
||||
|
||||
applicationList.value = data.rows || [];
|
||||
total.value = data.total || 0;
|
||||
|
||||
console.log('✅ 待审核申请列表获取成功 - 总数:', total.value, '当前页数据:', applicationList.value.length);
|
||||
|
||||
// 如果列表为空但有查询条件,提示用户
|
||||
if (total.value === 0 && applicationList.value.length === 0) {
|
||||
const hasSearchConditions = queryParams.value.positionName ||
|
||||
queryParams.value.studentName ||
|
||||
queryParams.value.studentNo ||
|
||||
queryParams.value.applicationDate;
|
||||
|
||||
if (hasSearchConditions) {
|
||||
console.log('📝 当前有搜索条件,列表为空可能是正常的');
|
||||
} else {
|
||||
console.log('📝 无搜索条件但列表为空,可能确实没有待审核数据');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 获取待审核申请列表失败:', error);
|
||||
ElMessage.error('获取数据失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
queryRef.value.resetFields();
|
||||
queryParams.value = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
positionName: '',
|
||||
studentName: '',
|
||||
studentNo: '',
|
||||
applicationDate: ''
|
||||
};
|
||||
getList();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: WorkStudyApplicationVO[]) => {
|
||||
ids.value = selection.map((item) => item.id!);
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleDetail = (row: WorkStudyApplicationVO) => {
|
||||
detailDialog.visible = true;
|
||||
detailDialog.applicationId = row.id as string;
|
||||
};
|
||||
|
||||
// 单个审核
|
||||
const handleReview = (row: WorkStudyApplicationVO, result: string) => {
|
||||
reviewDialog.visible = true;
|
||||
reviewDialog.title = `审核申请 - ${row.studentName}`;
|
||||
reviewDialog.type = 'single';
|
||||
reviewDialog.form = {
|
||||
applicationId: row.id as string,
|
||||
reviewResult: result,
|
||||
reviewComment: ''
|
||||
};
|
||||
};
|
||||
|
||||
// 批量审核
|
||||
const handleBatchReview = (result: string) => {
|
||||
if (!ids.value.length) {
|
||||
ElMessage.warning('请选择要审核的申请');
|
||||
return;
|
||||
}
|
||||
|
||||
reviewDialog.visible = true;
|
||||
reviewDialog.title = `批量${result === 'APPROVED' ? '通过' : '拒绝'}申请`;
|
||||
reviewDialog.type = 'batch';
|
||||
reviewDialog.form = {
|
||||
applicationId: '',
|
||||
reviewResult: result,
|
||||
reviewComment: ''
|
||||
};
|
||||
};
|
||||
|
||||
// 提交审核
|
||||
const submitReview = async () => {
|
||||
if (!reviewFormRef.value) return;
|
||||
|
||||
const valid = await reviewFormRef.value.validate();
|
||||
if (!valid) return;
|
||||
|
||||
try {
|
||||
reviewDialog.submitting = true;
|
||||
|
||||
if (reviewDialog.type === 'single') {
|
||||
await reviewApplication(reviewDialog.form);
|
||||
} else {
|
||||
await batchReviewApplications(ids.value, reviewDialog.form.reviewResult, reviewDialog.form.reviewComment);
|
||||
}
|
||||
|
||||
ElMessage.success('审核成功');
|
||||
reviewDialog.visible = false;
|
||||
getList();
|
||||
emit('refresh-statistics');
|
||||
} catch (error) {
|
||||
console.error('审核失败:', error);
|
||||
ElMessage.error('审核失败');
|
||||
} finally {
|
||||
reviewDialog.submitting = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露方法给父组件调用
|
||||
defineExpose({
|
||||
getList
|
||||
});
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pending-applications {
|
||||
.search-card {
|
||||
margin-bottom: 16px;
|
||||
border: none;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-form {
|
||||
.el-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.table-card {
|
||||
border: none;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px 20px 16px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
.table-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
|
||||
.el-icon {
|
||||
margin-right: 8px;
|
||||
color: #e6a23c;
|
||||
}
|
||||
}
|
||||
|
||||
.table-actions {
|
||||
.el-button + .el-button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-table {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
.name {
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
.text-gray {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.el-button + .el-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,360 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">
|
||||
<el-icon class="page-icon"><User /></el-icon>
|
||||
学院审核
|
||||
</h2>
|
||||
<p class="page-description">管理本学院学生的勤工俭学申请审核</p>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<el-row :gutter="20" class="statistics-row">
|
||||
<el-col :span="6">
|
||||
<el-card class="statistics-card pending" shadow="hover">
|
||||
<div class="card-content">
|
||||
<div class="card-icon">
|
||||
<el-icon><Clock /></el-icon>
|
||||
</div>
|
||||
<div class="card-info">
|
||||
<div class="card-title">待审核</div>
|
||||
<div class="card-value">{{ statistics.pendingCount || 0 }}</div>
|
||||
<div class="card-desc">需要处理的申请</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="statistics-card approved" shadow="hover">
|
||||
<div class="card-content">
|
||||
<div class="card-icon">
|
||||
<el-icon><Check /></el-icon>
|
||||
</div>
|
||||
<div class="card-info">
|
||||
<div class="card-title">已通过</div>
|
||||
<div class="card-value">{{ statistics.approvedCount || 0 }}</div>
|
||||
<div class="card-desc">学院审核通过</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="statistics-card rejected" shadow="hover">
|
||||
<div class="card-content">
|
||||
<div class="card-icon">
|
||||
<el-icon><Close /></el-icon>
|
||||
</div>
|
||||
<div class="card-info">
|
||||
<div class="card-title">已拒绝</div>
|
||||
<div class="card-value">{{ statistics.rejectedCount || 0 }}</div>
|
||||
<div class="card-desc">学院审核拒绝</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="statistics-card final-approved" shadow="hover">
|
||||
<div class="card-content">
|
||||
<div class="card-icon">
|
||||
<el-icon><Trophy /></el-icon>
|
||||
</div>
|
||||
<div class="card-info">
|
||||
<div class="card-title">最终通过</div>
|
||||
<div class="card-value">{{ statistics.finalApprovedCount || 0 }}</div>
|
||||
<div class="card-desc">最终审核通过</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 内容标签页 -->
|
||||
<el-card class="main-content" shadow="never">
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange" class="review-tabs">
|
||||
<el-tab-pane name="pending">
|
||||
<template #label>
|
||||
<span class="tab-label">
|
||||
<el-icon><Clock /></el-icon>
|
||||
待审核申请
|
||||
<el-badge v-if="statistics.pendingCount" :value="statistics.pendingCount" class="tab-badge" />
|
||||
</span>
|
||||
</template>
|
||||
<PendingApplications ref="pendingRef" @refresh-statistics="getStatistics" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="all">
|
||||
<template #label>
|
||||
<span class="tab-label">
|
||||
<el-icon><List /></el-icon>
|
||||
所有申请
|
||||
</span>
|
||||
</template>
|
||||
<AllApplications ref="allRef" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="debug" v-if="showDebugPanel">
|
||||
<template #label>
|
||||
<span class="tab-label">
|
||||
<el-icon><Tools /></el-icon>
|
||||
调试面板
|
||||
</span>
|
||||
</template>
|
||||
<DebugPanel />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CollegeReview">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { Clock, Check, Close, Trophy, User, List, Tools } from '@element-plus/icons-vue';
|
||||
import { getCollegeStatistics, getCollegeStatisticsByList, listCollegeApplications, type StatisticsVO } from '@/api/jcbt/workStudyApplicationCollegeReview';
|
||||
import PendingApplications from './components/PendingApplications.vue';
|
||||
import AllApplications from './components/AllApplications.vue';
|
||||
import DebugPanel from './components/DebugPanel.vue';
|
||||
|
||||
// 响应式数据
|
||||
const activeTab = ref('pending');
|
||||
const statistics = ref<StatisticsVO>({});
|
||||
const pendingRef = ref();
|
||||
const allRef = ref();
|
||||
const showDebugPanel = ref(false); // Added ref for debug panel visibility
|
||||
|
||||
// 获取统计数据
|
||||
const getStatistics = async () => {
|
||||
try {
|
||||
console.log('🔍 开始获取学院审核统计数据...');
|
||||
|
||||
// 方案1:使用原始统计API
|
||||
const { data } = await getCollegeStatistics();
|
||||
console.log('📊 统计API返回数据:', data);
|
||||
|
||||
// 方案2:通过列表API验证待审核数量
|
||||
try {
|
||||
const listResult = await listCollegeApplications({
|
||||
pageNum: 1,
|
||||
pageSize: 1,
|
||||
status: 'SUBMITTED'
|
||||
});
|
||||
console.log('📋 列表API验证 - 待审核数量:', listResult.data.total);
|
||||
|
||||
// 如果统计API的待审核数量与列表API不一致,使用列表API的数据
|
||||
if (data.pendingCount !== listResult.data.total) {
|
||||
console.warn('⚠️ 统计数据不一致!统计API:', data.pendingCount, '列表API:', listResult.data.total);
|
||||
data.pendingCount = listResult.data.total;
|
||||
console.log('✅ 已使用列表API数据修正待审核数量:', data.pendingCount);
|
||||
}
|
||||
} catch (listError) {
|
||||
console.warn('📋 列表API验证失败:', listError);
|
||||
}
|
||||
|
||||
statistics.value = data;
|
||||
console.log('✅ 最终统计数据:', statistics.value);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 获取统计数据失败:', error);
|
||||
statistics.value = {
|
||||
pendingCount: 0,
|
||||
approvedCount: 0,
|
||||
rejectedCount: 0,
|
||||
finalApprovedCount: 0,
|
||||
totalCount: 0
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 标签页切换
|
||||
const handleTabChange = (tabName: string) => {
|
||||
activeTab.value = tabName;
|
||||
if (tabName === 'pending' && pendingRef.value) {
|
||||
pendingRef.value.getList();
|
||||
} else if (tabName === 'all' && allRef.value) {
|
||||
allRef.value.getList();
|
||||
}
|
||||
};
|
||||
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
getStatistics();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.app-container {
|
||||
padding: 24px;
|
||||
background: #f5f7fa;
|
||||
min-height: calc(100vh - 84px);
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.page-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
|
||||
.page-icon {
|
||||
margin-right: 12px;
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.page-description {
|
||||
margin: 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-row {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
border: none;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
margin-right: 16px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.card-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.card-value {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 2px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
&.pending {
|
||||
.card-icon {
|
||||
background: linear-gradient(135deg, #ffeaa7 0%, #fab1a0 100%);
|
||||
color: #d63031;
|
||||
}
|
||||
.card-value {
|
||||
color: #e17055;
|
||||
}
|
||||
}
|
||||
|
||||
&.approved {
|
||||
.card-icon {
|
||||
background: linear-gradient(135deg, #55efc4 0%, #00b894 100%);
|
||||
color: #00695c;
|
||||
}
|
||||
.card-value {
|
||||
color: #00b894;
|
||||
}
|
||||
}
|
||||
|
||||
&.rejected {
|
||||
.card-icon {
|
||||
background: linear-gradient(135deg, #fd79a8 0%, #e84393 100%);
|
||||
color: #ad1457;
|
||||
}
|
||||
.card-value {
|
||||
color: #e84393;
|
||||
}
|
||||
}
|
||||
|
||||
&.final-approved {
|
||||
.card-icon {
|
||||
background: linear-gradient(135deg, #a29bfe 0%, #6c5ce7 100%);
|
||||
color: #4527a0;
|
||||
}
|
||||
.card-value {
|
||||
color: #6c5ce7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
border: none;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.review-tabs {
|
||||
:deep(.el-tabs__header) {
|
||||
padding: 20px 20px 0;
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__nav-wrap::after) {
|
||||
background: #e4e7ed;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__item) {
|
||||
padding: 0 20px;
|
||||
font-size: 16px;
|
||||
color: #606266;
|
||||
|
||||
&.is-active {
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-tabs__content) {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.tab-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.el-icon {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.tab-badge {
|
||||
margin-left: 8px;
|
||||
|
||||
:deep(.el-badge__content) {
|
||||
font-size: 11px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -131,12 +131,12 @@ watch(dialogVisible, (newVal) => {
|
||||
// 获取详情
|
||||
const getDetail = async () => {
|
||||
if (!props.applicationId) return;
|
||||
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await getCollegeApplication(props.applicationId);
|
||||
console.log('详情API响应:', response);
|
||||
|
||||
|
||||
// 处理不同的响应格式
|
||||
if (response.data) {
|
||||
if (response.data.data) {
|
||||
@@ -186,13 +186,13 @@ const getStatusType = (status: string) => {
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
|
||||
.content-text {
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
||||
.mb-20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@@ -170,41 +170,52 @@ const reviewFormRef = ref();
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
console.log('🔍 [调试] 开始获取待审核申请列表');
|
||||
console.log('🔍 [调试] 查询参数:', queryParams);
|
||||
|
||||
const response = await listPendingApplications(queryParams);
|
||||
console.log('API响应:', response);
|
||||
|
||||
// 处理不同的响应格式
|
||||
if (response.data) {
|
||||
// 如果是标准的TableDataInfo格式
|
||||
if (response.data.rows !== undefined) {
|
||||
applicationList.value = response.data.rows || [];
|
||||
total.value = response.data.total || 0;
|
||||
}
|
||||
// 如果是包装在R<TableDataInfo>格式中
|
||||
else if (response.data.data && response.data.data.rows !== undefined) {
|
||||
applicationList.value = response.data.data.rows || [];
|
||||
total.value = response.data.data.total || 0;
|
||||
}
|
||||
// 如果直接是数组数据
|
||||
else if (Array.isArray(response.data)) {
|
||||
applicationList.value = response.data;
|
||||
total.value = response.data.length;
|
||||
}
|
||||
// 其他情况
|
||||
else {
|
||||
console.warn('未识别的响应格式:', response.data);
|
||||
console.log('🔍 [调试] API原始响应:', response);
|
||||
|
||||
// 根据实际响应格式处理数据
|
||||
// 从调试输出看,response直接包含{total: 1, rows: Array(1), code: 200, msg: '查询成功'}
|
||||
if (response && typeof response === 'object') {
|
||||
console.log('🔍 [调试] 响应对象键名:', Object.keys(response));
|
||||
|
||||
if (response.rows !== undefined && response.total !== undefined) {
|
||||
console.log('🔍 [调试] ✅ 识别为TableDataInfo格式');
|
||||
console.log('🔍 [调试] rows数据:', response.rows);
|
||||
console.log('🔍 [调试] total数量:', response.total);
|
||||
applicationList.value = response.rows || [];
|
||||
total.value = response.total || 0;
|
||||
} else {
|
||||
console.warn('🔍 [调试] ❌ 响应格式不匹配,缺少rows或total字段');
|
||||
applicationList.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
} else {
|
||||
console.warn('🔍 [调试] ❌ 响应不是对象类型');
|
||||
applicationList.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取待审核申请列表失败:', error);
|
||||
if (error.message.includes('404')) {
|
||||
|
||||
console.log('🔍 [调试] 最终设置的applicationList长度:', applicationList.value.length);
|
||||
console.log('🔍 [调试] 最终设置的total:', total.value);
|
||||
|
||||
if (applicationList.value.length > 0) {
|
||||
console.log('🔍 [调试] 第一条数据示例:', applicationList.value[0]);
|
||||
} else {
|
||||
console.log('🔍 [调试] ⚠️ 列表为空!');
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('🔍 [调试] 获取待审核申请列表失败:', error);
|
||||
if (error?.response?.data) {
|
||||
console.error('🔍 [调试] 错误详情:', error.response.data);
|
||||
}
|
||||
|
||||
if (error?.message?.includes('404')) {
|
||||
ElMessage.error('无法获取待审核申请列表,请检查您是否有学院审核权限');
|
||||
} else if (error.message.includes('403')) {
|
||||
} else if (error?.message?.includes('403')) {
|
||||
ElMessage.error('权限不足,无法访问学院审核功能');
|
||||
} else {
|
||||
ElMessage.error('获取待审核申请列表失败,请稍后重试');
|
||||
|
||||
Reference in new issue
Block a user