Files
zhxg_app/pages/finance/knzzGl/index.vue

872 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="knzz-gl">
<!-- 申请列表 -->
<view class="app-container">
<!-- 数据列表滚动容器 -->
<scroll-view class="list-container" id="list-container" scroll-y @scrolltolower="handleLoadMore"
lower-threshold="50" enhanced :show-scrollbar="false">
<!-- 初始加载状态 -->
<uni-load-more :status="loading ? 'loading' : 'done'" v-if="loading" class="loading-wrap"></uni-load-more>
<!-- 空数据状态 -->
<view class="empty-state" v-if="!loading && !hasData">
<uni-icons type="empty" size="80" color="#c0c4cc"></uni-icons>
<text class="empty-text">暂无励志奖学金申请数据</text>
<text class="empty-tip" v-if="roleGroup.includes('学生')">点击右下角"+"按钮提交新申请</text>
</view>
<!-- 数据列表 -->
<view class="card-list" v-if="!loading && hasData">
<view class="apply-card" v-for="(item, index) in validDataList" :key="index" hover-class="card-hover"
:hover-stop-propagation="true">
<!-- 卡片头部 -->
<view class="card-header">
<view class="apply-no">
<text class="label-text">学年</text>
<text class="no-text">{{ getSafeValue(item, 'stuYearName', '无') }}</text>
</view>
<view class="status-wrap">
<view class="status-label" :class="getStatusClass(item)">
{{ getStatusText(item) }}
</view>
</view>
</view>
<!-- 卡片主体折叠面板 -->
<uni-collapse accordion v-model="item.activeCollapse">
<uni-collapse-item title="基本信息" name="basic" class="collapse-item">
<view class="info-grid">
<view class="info-item"><text class="label">学号</text><text
class="value">{{ getSafeValue(item, 'stuNo', '-') }}</text></view>
<view class="info-item"><text class="label">姓名</text><text
class="value">{{ getSafeValue(item, 'stuName', '-') }}</text></view>
<view class="info-item"><text class="label">学院</text><text
class="value">{{ getSafeValue(item, 'deptName', '-') }}</text></view>
<view class="info-item"><text class="label">班级</text><text
class="value">{{ getSafeValue(item, 'className', '-') }}</text></view>
</view>
</uni-collapse-item>
<uni-collapse-item title="申请信息" name="apply" class="collapse-item">
<view class="info-form">
<view class="form-item"><text class="label">入学时间</text><text
class="value">{{ getSafeValue(item, 'inTime', '-') }}</text></view>
<view class="form-item"><text class="label">曾获资助/奖励</text><text
class="value">{{ getSafeValue(item, 'helpHis', '-') }}</text></view>
<view class="form-item"><text class="label">家庭人口数</text><text
class="value">{{ getSafeValue(item, 'familyNum', '-') }}</text></view>
<view class="form-item"><text class="label">家庭月总收入</text><text
class="value">{{ getSafeValue(item, 'monthMoney', '-') }}</text></view>
<view class="form-item"><text class="label">人均月收入</text><text
class="value">{{ getSafeValue(item, 'perMoney', '-') }}</text></view>
<view class="form-item"><text class="label">收入来源</text><text
class="value">{{ getSafeValue(item, 'moneySrc', '-') }}</text></view>
<view class="form-item"><text class="label">专业排名</text><text
class="value">{{ getSafeValue(item, 'majorRank', '-') }}</text></view>
<view class="form-item"><text class="label">专业人数</text><text
class="value">{{ getSafeValue(item, 'majorNum', '-') }}</text></view>
<view class="form-item"><text class="label">班级排名</text><text
class="value">{{ getSafeValue(item, 'classRank', '-') }}</text></view>
<view class="form-item"><text class="label">班级人数</text><text
class="value">{{ getSafeValue(item, 'classNum', '-') }}</text></view>
<view class="form-item"><text class="label">必修课门数</text><text
class="value">{{ getSafeValue(item, 'bxk', '-') }}</text></view>
<view class="form-item"><text class="label">及格门数</text><text
class="value">{{ getSafeValue(item, 'jg', '-') }}</text></view>
<view class="form-item"><text class="label">申请理由</text><text
class="value">{{ getSafeValue(item, 'applyReason', '-') }}</text></view>
</view>
</uni-collapse-item>
<uni-collapse-item title="审核意见" name="approval" class="collapse-item">
<view class="approval-list">
<view class="approval-item">
<text class="approval-node">辅导员审核意见</text>
<text class="approval-result" :class="getReviewClass(item, 'fdyCmt')">
{{ getReviewDetailText(item, 'fdyCmt') }}
</text>
<text class="approval-time" v-if="getSafeValue(item, 'fdyDate')">
{{ getSafeValue(item, 'fdyDate') }}
</text>
</view>
<view class="approval-item">
<text class="approval-node">院系审核意见</text>
<text class="approval-result" :class="getReviewClass(item, 'xwCmt')">
{{ getReviewDetailText(item, 'xwCmt') }}
</text>
<text class="approval-time" v-if="getSafeValue(item, 'xwDate')">
{{ getSafeValue(item, 'xwDate') }}
</text>
</view>
<view class="approval-item">
<text class="approval-node">学工审核意见</text>
<text class="approval-result" :class="getReviewClass(item, 'xgCmt')">
{{ getReviewDetailText(item, 'xgCmt') }}
</text>
<text class="approval-time" v-if="getSafeValue(item, 'xgDate')">
{{ getSafeValue(item, 'xgDate') }}
</text>
</view>
</view>
</uni-collapse-item>
</uni-collapse>
<!-- 卡片操作区 -->
<view class="card-actions">
<view>
<uni-button type="text" size="mini" @click="detail(item, '修改')"
v-if="item.applyStatus == '10'">
<uni-icons type="refresh" size="14" class="mr-5"></uni-icons>重新提交
</uni-button>
<uni-button type="text" size="mini" @click="detail(item, '修改')"
v-if="item.applyStatus == '1'">
<uni-icons type="edit" size="14" class="mr-5"></uni-icons>修改
</uni-button>
<uni-button type="text" size="mini" @click="handleDelete(item)"
v-if="item.applyStatus == '1'">
<uni-icons type="close" size="14" class="mr-5"></uni-icons>取消申请
</uni-button>
</view>
<view>
<uni-button type="text" size="mini" @click="detail(item, '详情')" class="detail-btn"
:disabled="!getSafeValue(item, 'id')">
<uni-icons type="eye" size="14" class="mr-5"></uni-icons>查看
</uni-button>
</view>
</view>
</view>
</view>
<!-- 加载更多状态 -->
<view class="load-more-wrap" v-if="hasData">
<uni-load-more :status="loadingMore ? 'loading' : (hasMore ? 'more' : 'nomore')"
:content-text="loadMoreText"></uni-load-more>
</view>
</scroll-view>
</view>
<!-- 添加申请按钮 -->
<view class="add" @click="applyGl" v-if="roleGroup.includes('学生')">{{ '+' }}</view>
</view>
</template>
<script>
import { listOwn, doEdit, doCancel, doReApply, selectTestStu, listDisciplinaryApplication, getDicts, listOwnScoreClassRankByTag } from '@/api/finance/knzzGl';
import { getUserProfile } from '@/api/system/user';
export default {
name: "KnzzGlList",
data() {
return {
// 加载状态
loading: true,
loadingMore: false,
hasMore: true,
// 总数据量
totalCount: 0,
// 原始数据
glApplyList: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 5
},
// 用户角色
roleGroup: '',
user: {},
// 加载更多文本
loadMoreText: {
contentdown: '上拉加载更多',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了'
}
};
},
computed: {
// 过滤后的有效数据列表
validDataList() {
return this.glApplyList.filter(item => {
return item !== null && item !== undefined && typeof item === 'object';
}).map(item => {
if (!item.hasOwnProperty('activeCollapse')) {
item.activeCollapse = 'basic';
}
return item;
});
},
// 是否有有效数据
hasData() {
return this.validDataList.length > 0;
}
},
onShow() {
this.getUser();
},
methods: {
/** 安全取值方法 */
getSafeValue(obj, key, defaultValue = '') {
if (!obj || typeof obj !== 'object') return defaultValue;
return obj[key] !== undefined && obj[key] !== null ? obj[key] : defaultValue;
},
/** 获取状态样式类 */
getStatusClass(item) {
const status = this.getSafeValue(item, 'applyStatus');
const typeMap = {
'1': 'status-warning',
'2': 'status-success',
'3': 'status-success',
'4': 'status-success',
'5': 'status-success',
'10': 'status-error'
};
return typeMap[status] || 'status-default';
},
/** 获取状态文本 */
getStatusText(item) {
const status = this.getSafeValue(item, 'applyStatus');
const statusMap = {
'1': '待审核',
'2': '辅导员审核通过',
'3': '院系审核通过',
'4': '学工审核通过',
'5': '审核通过',
'10': '已驳回'
};
return statusMap[status] || '未知状态';
},
/** 获取审核意见样式类 */
getReviewClass(item, type = 'fdyCmt') {
const reviewResult = this.getSafeValue(item, type);
if (!reviewResult) return 'status-default';
return 'status-success';
},
/** 获取详细审核意见文本 */
getReviewDetailText(item, type = 'fdyCmt') {
const reviewResult = this.getSafeValue(item, type);
if (!reviewResult) return '未审核';
return reviewResult;
},
/** 时间格式化 */
parseTime(time, format = '{y}-{m}-{d}') {
if (!time) return '';
try {
const date = new Date(time);
if (isNaN(date.getTime())) return '';
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return format.replace('{y}', year).replace('{m}', month).replace('{d}', day);
} catch (error) {
return '';
}
},
/** 查询数据 */
getList(isRefresh = false) {
if (isRefresh) {
this.loading = true;
this.hasMore = true;
this.queryParams.pageNum = 1;
this.glApplyList = [];
} else {
this.loadingMore = true;
}
listOwn(this.queryParams).then(response => {
const res = response || {};
const newData = Array.isArray(res.rows) ? res.rows : [];
this.totalCount = Number(res.total) || 0;
if (isRefresh) {
this.glApplyList = newData;
} else {
this.glApplyList = [...this.glApplyList, ...newData];
}
// 更新状态
this.loading = false;
this.loadingMore = false;
this.hasMore = this.glApplyList.length < this.totalCount;
}).catch((error) => {
console.error('数据加载失败:', error);
this.loading = false;
this.loadingMore = false;
uni.showToast({
title: '数据加载失败',
icon: 'none'
});
});
},
/** 获取用户信息 */
getUser() {
getUserProfile().then(response => {
const res = response || {};
this.user = res.data || {};
this.roleGroup = res.roleGroup || '';
// 获取申请列表
this.getList(true);
}).catch((error) => {
console.error('用户信息获取失败:', error);
this.loading = false;
uni.showToast({
title: '用户信息加载失败',
icon: 'none'
});
});
},
/** 加载更多 */
handleLoadMore() {
if (this.loadingMore || !this.hasMore) return;
this.queryParams.pageNum += 1;
this.getList(false);
},
/** 查看详情 */
detail(item, text) {
const id = this.getSafeValue(item, 'id');
if (!id) {
uni.showToast({
title: '数据异常,无法查看详情',
icon: 'none'
});
return;
}
// 使用本地存储传递item数据到详情页面避免url长度限制
uni.setStorageSync('glApplyItem', item);
if (text == '详情') {
uni.navigateTo({
url: `/pages/finance/knzzGl/apply?id=${id}&type=detail`
});
} else {
// 检查是否是重新提交(已驳回状态)
const isReApply = this.getSafeValue(item, 'applyStatus') === '10';
uni.navigateTo({
url: `/pages/finance/knzzGl/apply?id=${id}&isReApply=${isReApply}`
});
}
},
/** 删除申请 */
handleDelete(item) {
uni.showModal({
title: '确认删除',
content: '确定要删除该申请吗?',
success: (res) => {
if (res.confirm) {
doCancel(item.id).then(() => {
uni.showToast({
title: '删除成功',
icon: 'success'
});
this.getList(true);
}).catch(() => {
uni.showToast({
title: '删除失败',
icon: 'none'
});
});
}
}
});
},
// 跳转到申请页面
applyGl() {
this.checkAllConditions();
},
// 检查所有申请条件
async checkAllConditions() {
uni.showLoading({
title: '正在校验资格...',
mask: true
});
try {
const [sportResult, disciplinaryResult, scoreResult] = await Promise.all([
this.fetchTestScoreData(),
this.fetchcfwjc(),
this.fetchcjpm()
]);
uni.hideLoading();
if (sportResult && disciplinaryResult && scoreResult) {
// 所有条件都满足,跳转到申请页面
uni.navigateTo({
url: './apply'
});
}
} catch (error) {
console.error('资格校验失败:', error);
uni.hideLoading();
uni.showToast({
title: '资格校验失败,请稍后重试',
icon: 'none'
});
}
},
// 获取体能测试成绩
async fetchTestScoreData() {
try {
const res = await selectTestStu();
if (res.code === 200 && res.rows && res.rows.length > 0) {
const testScore = res.rows[res.rows.length - 1].testScore;
if (testScore >= 60) {
return true;
} else if (testScore === 0) {
uni.showToast({
title: '当前学年没有您的体能成绩',
icon: 'none'
});
return false;
} else {
uni.showToast({
title: '您的体能成绩不达标',
icon: 'none'
});
return false;
}
} else {
uni.showToast({
title: '请先完成体能测试',
icon: 'none'
});
return false;
}
} catch (error) {
console.error('体能测试数据获取失败:', error);
uni.showToast({
title: '体能测试数据获取失败',
icon: 'none'
});
return false;
}
},
// 获取处分记录
async fetchcfwjc() {
try {
const [dictResponse, disciplinaryResponse] = await Promise.all([
getDicts('rt_penalty_status'),
listDisciplinaryApplication()
]);
if (Array.isArray(disciplinaryResponse.rows) && disciplinaryResponse.rows.length > 0) {
const penaltyStatus = disciplinaryResponse.rows[0].penaltyStatus;
const dictData = dictResponse.data || [];
// 查找对应处分状态的字典项
const处分中 = dictData.find(item => item.dictSort === '1');
const已解除 = dictData.find(item => item.dictSort === '2');
const解除申请中 = dictData.find(item => item.dictSort === '3');
const处分申请中 = dictData.find(item => item.dictSort === '4');
if (penaltyStatus === '1') {
uni.showToast({
title: '您目前处于处分中,无法申请',
icon: 'none'
});
return false;
} else if (penaltyStatus === '3') {
uni.showToast({
title: '您的解除处分申请正在处理中',
icon: 'none'
});
return false;
} else if (penaltyStatus === '4') {
uni.showToast({
title: '您的处分申请正在处理中',
icon: 'none'
});
return false;
} else {
return true;
}
} else {
return true;
}
} catch (error) {
console.error('处分状态获取失败:', error);
uni.showToast({
title: '处分状态获取失败',
icon: 'none'
});
return false;
}
},
// 获取成绩排名
async fetchcjpm() {
try {
const res = await listOwnScoreClassRankByTag('NLZJ');
if (res.code === 200 && res.data && res.data.length > 0) {
const data = res.data[0];
if (data.stuMajorRank != null && data.majorCount != null) {
const stuRank = Math.round(Math.round(data.stuMajorRank / data.majorCount * 10000) / 100);
if (stuRank <= 30) {
return true;
} else {
uni.showToast({
title: '您的学业成绩未达到专业前30%',
icon: 'none'
});
return false;
}
}
}
uni.showToast({
title: '无法获取您的专业排名数据',
icon: 'none'
});
return false;
} catch (error) {
console.error('成绩排名获取失败:', error);
uni.showToast({
title: '成绩排名获取失败',
icon: 'none'
});
return false;
}
},
// 重新提交申请
handleReApply(item) {
uni.showModal({
title: '确认重新提交',
content: '确定要重新提交该申请吗?',
success: (res) => {
if (res.confirm) {
// 调用重新提交接口
doReApply(item).then(response => {
const res = response || {};
if (res.code === 200 || res.code === 0) {
uni.showToast({
title: '重新提交成功',
icon: 'success'
});
this.getList(true);
} else {
uni.showToast({
title: res.msg || '重新提交失败',
icon: 'none'
});
}
}).catch(error => {
console.error('重新提交申请失败:', error);
uni.showToast({
title: error.response?.data?.msg || error.message || '重新提交失败',
icon: 'none'
});
});
}
}
});
}
}
};
</script>
<style scoped>
.knzz-gl {
background-color: #F3F4F6;
min-height: 100vh;
padding-bottom: 20rpx;
}
.page-header {
background-color: #4A90E2;
color: white;
padding: 30rpx 20rpx;
text-align: center;
.title {
font-size: 36rpx;
font-weight: bold;
}
}
.app-container {
padding-bottom: 10rpx;
}
.list-container {
width: 100%;
min-height: calc(100vh - 300rpx);
}
.loading-wrap {
padding: 20rpx;
text-align: center;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
color: #c0c4cc;
}
.empty-text {
margin-top: 20rpx;
font-size: 28rpx;
}
.empty-tip {
margin-top: 10rpx;
font-size: 24rpx;
color: #909399;
}
.card-list {
width: 100%;
padding: 5px 15px 0px;
}
.apply-card {
background-color: white;
margin-bottom: 10px;
border-radius: 10px;
overflow: hidden;
}
.card-hover {
opacity: 0.8;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 24rpx;
border-bottom: 1px solid #f0f0f0;
}
.apply-no {
display: flex;
align-items: center;
}
.label-text {
font-size: 26rpx;
color: #606266;
margin-right: 10rpx;
}
.no-text {
font-size: 28rpx;
color: #303133;
font-weight: 500;
}
.status-wrap {
display: flex;
align-items: center;
gap: 10rpx;
}
.status-label {
padding: 4rpx 12rpx;
border-radius: 10rpx;
font-size: 22rpx;
color: white;
}
.status-info {
background-color: #909399;
}
.status-warning {
background-color: #e6a23c;
}
.status-success {
background-color: #67c23a;
}
.status-error {
background-color: #f56c6c;
}
.status-default {
background-color: #909399;
}
.collapse-item {
padding: 0;
}
.info-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15rpx;
padding: 20rpx 24rpx;
}
.info-item {
display: flex;
align-items: center;
}
.info-item .label {
font-size: 26rpx;
color: #606266;
width: 100rpx;
}
.info-item .value {
font-size: 26rpx;
color: #303133;
flex: 1;
}
.info-form {
padding: 20rpx 24rpx;
}
.form-item {
display: flex;
align-items: flex-start;
margin-bottom: 15rpx;
}
.form-item:last-child {
margin-bottom: 0;
}
.form-item .label {
font-size: 26rpx;
color: #606266;
width: 140rpx;
padding-top: 5rpx;
}
.form-item .value {
font-size: 26rpx;
color: #303133;
flex: 1;
word-break: break-word;
}
.split-text {
margin: 0 10rpx;
color: #909399;
}
.approval-list {
padding: 20rpx 24rpx;
}
.approval-item {
margin-bottom: 15rpx;
}
.approval-item:last-child {
margin-bottom: 0;
}
.approval-node {
font-size: 26rpx;
color: #606266;
margin-right: 10rpx;
}
.approval-result {
font-size: 26rpx;
padding: 4rpx 12rpx;
border-radius: 10rpx;
color: white;
}
.approval-time {
font-size: 24rpx;
color: #909399;
margin-left: 10rpx;
margin-top: 5rpx;
display: block;
}
.empty-approval {
padding: 20rpx 24rpx;
color: #909399;
font-size: 26rpx;
text-align: center;
}
.card-actions {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15rpx 24rpx;
border-top: 1px solid #f0f0f0;
}
.detail-btn {
color: #1890FF;
}
.load-more-wrap {
padding: 20rpx;
text-align: center;
}
.add {
position: fixed;
right: 30rpx;
bottom: 30rpx;
width: 80rpx;
height: 80rpx;
background-color: #1890FF;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 48rpx;
box-shadow: 0 4rpx 20rpx rgba(24, 144, 255, 0.3);
}
.info-area {
margin: 20rpx;
background-color: white;
border-radius: 10rpx;
padding: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
.info-header {
margin-bottom: 20rpx;
.info-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
}
.info-content {
.info-text {
display: block;
font-size: 26rpx;
color: #666;
margin-bottom: 15rpx;
line-height: 1.5;
}
}
}
</style>