提交代码

This commit is contained in:
zlwu committed 2025-08-22 00:42:29 +08:00
1 parent f71eada96c
commit dc8193e3c4
11 files changed
+6383 -27

No files matched your search

+5 -1
View File
@@ -171,4 +171,8 @@ update/*
本地临时文件目录/*
docs/变更管理/
学生考核模块的提示词.md
学生考核模块的上下文信息临时存储.md
学生考核模块的上下文信息临时存储.md
学生违纪记录界面-移动端.md
学生考核申请界面-移动端.md
移动端附件组件规范.md
移动端违纪记录模块开发规范.md
+24
View File
@@ -222,6 +222,30 @@
"navigationBarTitleText": "新生报名登记",
"navigationStyle": "custom"
}
},
{
"path": "pages/xskh/wjjl/xswjjl",
"style": {
"navigationBarTitleText": "违纪记录(学生)",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xskh/wjjl/revokeApplication",
"style": {
"navigationBarTitleText": "撤销申请",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/xskh/khsq/khsq",
"style": {
"navigationBarTitleText": "考核申请",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
}
],
"globalStyle": {
+45 -1
View File
@@ -220,6 +220,34 @@
/>
</template>
</ServiceItem>
<ServiceItem
v-if="hasPermi(['student:violation:list'])"
title="违纪记录(学生)"
@click="gotoStudentViolation"
>
<template #icon>
<image
src="/static/00143.png"
mode="aspectFit"
style="width: 24px; height: 24px"
/>
</template>
</ServiceItem>
<ServiceItem
v-if="hasPermi(['student:assessment:apply'])"
title="考核申请"
@click="gotoAssessmentApply"
>
<template #icon>
<image
src="/static/00138.png"
mode="aspectFit"
style="width: 24px; height: 24px"
/>
</template>
</ServiceItem>
</GridLayout>
</Card>
@@ -645,7 +673,9 @@ export default {
this.hasPermi(['student:internship:list']) ||
this.hasPermi(['student:dorm:violation:list']) ||
this.hasPermi(['student:visit:list']) ||
this.hasPermi(['student:dorm:hygiene:list'])
this.hasPermi(['student:dorm:hygiene:list']) ||
this.hasPermi(['student:violation:list']) ||
this.hasPermi(['student:assessment:apply'])
);
},
@@ -880,6 +910,20 @@ export default {
});
},
// 跳转到学生违纪记录页面
gotoStudentViolation() {
uni.navigateTo({
url: '/pages/xskh/wjjl/xswjjl',
});
},
// 跳转到考核申请页面
gotoAssessmentApply() {
uni.navigateTo({
url: '/pages/xskh/khsq/khsq',
});
},
// 图片加载成功处理
handleImageLoad(event) {
console.log('✅ 轮播图片加载成功:', event.detail);
@@ -394,15 +394,8 @@
size="mini"
type="error"
plain
@click.stop.prevent="
handleRemoveFile(
file,
index +
attachmentList.filter((f) => isImageFile(f))
.length,
$event,
)
"
@tap="handleRemoveFile(file, index)"
@click.stop.prevent="handleRemoveFileClick(file, index, $event)"
>
删除
</u-button>
@@ -1350,7 +1343,23 @@ export default {
}
this.handleDownloadAttachment(file);
},
// 文档删除按钮点击处理(安全的事件处理)
handleRemoveFileClick(file, index, event) {
console.log('销假文档删除按钮被点击:', file, index);
// 强制阻止事件冒泡和默认行为
if (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}
// 调用实际的删除方法
this.handleRemoveFile(file, index);
},
handleRemoveFile(file, index, event) {
// 强制阻止事件冒泡和默认行为(带空值检查)
if (event) {
event.stopPropagation && event.stopPropagation();
event.preventDefault && event.preventDefault();
@@ -387,14 +387,9 @@
size="mini"
type="error"
plain
@tap="handleRemoveFile(file, index)"
@click.stop.prevent="
handleRemoveFile(
file,
index +
attachmentList.filter((f) => isImageFile(f))
.length,
$event,
)
handleRemoveFileClick(file, index, $event)
"
>
删除
@@ -2496,11 +2491,26 @@ export default {
this.attachmentList.splice(index, 1);
},
// 文档删除按钮点击处理(安全的事件处理)
handleRemoveFileClick(file, index, event) {
console.log('文档删除按钮被点击:', file, index);
// 强制阻止事件冒泡和默认行为
if (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}
// 调用实际的删除方法
this.handleRemoveFile(file, index);
},
// 手动删除文件
handleRemoveFile(file, index, event) {
console.log('=== 手动删除文件 ===', file, index);
// 强制阻止事件冒泡和默认行为
// 强制阻止事件冒泡和默认行为(带空值检查)
if (event) {
event.stopPropagation();
event.preventDefault();
+29 -8
View File
@@ -525,14 +525,9 @@
size="mini"
type="error"
plain
@tap="handleRemoveFile(file, index)"
@click.stop.prevent="
handleRemoveFile(
file,
index +
attachmentList.filter((f) => isImageFile(f))
.length,
$event,
)
handleRemoveFileClick(file, index, $event)
"
>
删除
@@ -2438,7 +2433,12 @@ export default {
// 删除文档
handleRemoveFile(file, index, event) {
event.stopPropagation();
// 强制阻止事件冒泡和默认行为(带空值检查)
if (event) {
event.stopPropagation();
event.preventDefault();
}
uni.showModal({
title: '确认删除',
content: '确定要删除这个文件吗?',
@@ -2447,6 +2447,12 @@ export default {
const fileIndex = this.attachmentList.findIndex((f) => f.uid === file.uid);
if (fileIndex > -1) {
this.attachmentList.splice(fileIndex, 1);
// 清理blob URL,防止内存泄漏
if (file.blobUrl && file.blobUrl.startsWith('blob:')) {
URL.revokeObjectURL(file.blobUrl);
}
uni.showToast({
title: '文件删除成功',
icon: 'success',
@@ -2457,6 +2463,21 @@ export default {
});
},
// 文档删除按钮点击处理(安全的事件处理)
handleRemoveFileClick(file, index, event) {
console.log('文档删除按钮被点击:', file, index);
// 强制阻止事件冒泡和默认行为
if (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}
// 调用实际的删除方法
this.handleRemoveFile(file, index);
},
// 重试上传
async retryUploadFile(file, event) {
console.log('=== 重试上传文件 ===', file);
+87
View File
@@ -0,0 +1,87 @@
<template>
<view class="container">
<view class="page-header">
<text class="page-title">考核申请</text>
</view>
<view class="content">
<view class="notice-card">
<u-icon name="info-circle" size="20" color="#1890ff" />
<text class="notice-text">考核申请功能正在开发中...</text>
</view>
<!-- 这里可以添加考核申请的具体内容 -->
<view class="form-section">
<!-- 申请表单内容将在这里添加 -->
</view>
</view>
</view>
</template>
<script>
export default {
name: 'AssessmentApply',
data() {
return {
// 页面数据
};
},
onLoad(options) {
// 页面加载时的处理
console.log('考核申请页面加载');
},
methods: {
// 页面方法
},
};
</script>
<style lang="scss" scoped>
.container {
min-height: 100vh;
background-color: #f5f5f5;
}
.page-header {
background: linear-gradient(135deg, #007aff, #0051d9);
padding: calc(var(--status-bar-height) + 16px) 16px 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.page-title {
font-size: 20px;
font-weight: 600;
color: #ffffff;
text-align: center;
display: block;
}
.content {
padding: 16px;
}
.notice-card {
background: #ffffff;
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 12px;
}
.notice-text {
color: #666666;
font-size: 14px;
flex: 1;
}
.form-section {
background: #ffffff;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
min-height: 200px;
}
</style>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+476
View File
@@ -0,0 +1,476 @@
<template>
<view class="discipline-record-container">
<!-- 顶部导航 -->
<u-navbar title="学生违纪记录" :border-bottom="false" bg-color="#3c9cff" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</u-navbar>
<!-- 加载状态 -->
<u-loading-page :loading="loading" loading-text="加载中..."></u-loading-page>
<!-- 违纪记录列表 -->
<view v-if="!loading" class="content-wrapper">
<!-- 空状态 -->
<view v-if="disciplineRecordList.length === 0" class="empty-container">
<u-empty text="暂无违纪记录" mode="data"></u-empty>
</view>
<!-- 违纪记录卡片列表 -->
<view v-else class="record-list">
<view
v-for="(item, index) in disciplineRecordList"
:key="item.id || index"
class="record-card"
>
<!-- 卡片头部 -->
<view class="card-header">
<view class="record-title">{{ item.disciplineTypeName || '-' }}</view>
<view class="record-status">
<u-tag
:text="getDictLabel(studentDisciplineStatus, item.status)"
:type="getDictTagType(studentDisciplineStatus, item.status)"
size="mini"
></u-tag>
</view>
</view>
<!-- 卡片内容 -->
<view class="card-content">
<view class="info-item">
<text class="label">学号:</text>
<text class="value">{{ item.studentNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">学生姓名:</text>
<text class="value">{{ item.studentName || '-' }}</text>
</view>
<view class="info-item">
<text class="label">学院名称:</text>
<text class="value">{{ item.collegeName || '-' }}</text>
</view>
<view class="info-item">
<text class="label">专业名称:</text>
<text class="value">{{ item.majorName || '-' }}</text>
</view>
<view class="info-item">
<text class="label">班级名称:</text>
<text class="value">{{ item.className || '-' }}</text>
</view>
<view class="info-item">
<text class="label">违纪处分日期:</text>
<text class="value">{{ item.disciplineDate || '-' }}</text>
</view>
<view class="info-item">
<text class="label">扣分分值:</text>
<text class="value score-text">
{{
item.scoreDeduction
? parseFloat(item.scoreDeduction).toFixed(2)
: '0.00'
}}
</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="card-actions">
<view class="action-btn info-btn" @click="handleView(item)">
<u-icon name="eye" size="16" color="#3c9cff"></u-icon>
<text class="btn-text">查看</text>
</view>
<view
v-if="item.status === '1'"
class="action-btn warning-btn"
@click="handleRevokeApply(item)"
>
<u-icon name="edit-pen" size="16" color="#ff9900"></u-icon>
<text class="btn-text">撤销申请</text>
</view>
<view class="action-btn success-btn" @click="handleViewRevoke(item)">
<u-icon name="file-text" size="16" color="#5ac725"></u-icon>
<text class="btn-text">查看撤销申请</text>
</view>
</view>
</view>
</view>
<!-- 滚动加载提示 -->
<view v-if="hasMore && disciplineRecordList.length > 0" class="load-more">
<u-loadmore :status="loadStatus" @loadmore="loadMore"></u-loadmore>
</view>
</view>
<!-- 违纪记录详情弹窗 -->
<DisciplineRecordForm ref="disciplineRecordFormRef" @success="handleSuccess" />
<!-- 查看撤销申请弹窗 -->
<RevokeApplicationView ref="revokeApplicationViewRef" />
</view>
</template>
<script>
import { request } from '@/common/request.js';
import { getDictLabel, getDictTagType, getDict } from '@/common/dict.js';
import DisciplineRecordForm from './DisciplineRecordForm.vue';
import RevokeApplicationView from './RevokeApplicationView.vue';
export default {
name: 'StudentDisciplineRecord',
components: {
DisciplineRecordForm,
RevokeApplicationView,
},
data() {
return {
loading: false,
disciplineRecordList: [],
total: 0,
hasMore: true,
loadStatus: 'loadmore',
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
},
// 字典数据
studentDisciplineStatus: [],
};
},
async created() {
await this.loadDictData();
await this.getList();
},
// 页面显示时刷新数据
onShow() {
// 从撤销申请页面返回时刷新列表
if (this.disciplineRecordList.length > 0) {
this.handleSuccess();
}
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack();
},
// 加载字典数据
async loadDictData() {
try {
this.studentDisciplineStatus = await getDict('student_discipline_status');
} catch (error) {
console.error('加载字典数据失败:', error);
}
},
// 获取字典标签
getDictLabel(dictData, value) {
return getDictLabel(dictData, value);
},
// 获取字典标签类型
getDictTagType(dictData, value) {
const fallbackMap = {
1: 'success', // 生效
0: 'info', // 失效
};
return getDictTagType(dictData, value, fallbackMap);
},
// 获取学生违纪处分记录列表
async getList(loadMore = false) {
if (!loadMore) {
this.loading = true;
}
try {
const res = await request({
url: '/disciplineRecord/disciplineRecord/my',
method: 'GET',
params: this.queryParams,
});
if (res && res.code === 200) {
const rows = res.rows || [];
if (loadMore) {
// 加载更多时追加数据
this.disciplineRecordList = this.disciplineRecordList.concat(rows);
} else {
// 首次加载或刷新时替换数据
this.disciplineRecordList = rows;
}
this.total = res.total || 0;
// 判断是否还有更多数据
if (rows.length < this.queryParams.pageSize) {
this.hasMore = false;
this.loadStatus = 'nomore';
} else {
this.hasMore = true;
this.loadStatus = 'loadmore';
}
} else {
if (!loadMore) {
this.disciplineRecordList = [];
this.total = 0;
}
this.hasMore = false;
this.loadStatus = 'nomore';
uni.showToast({
title: res?.msg || '获取数据失败',
icon: 'none',
});
}
} catch (error) {
console.error('获取学生违纪记录失败:', error);
if (!loadMore) {
this.disciplineRecordList = [];
this.total = 0;
}
this.hasMore = false;
this.loadStatus = 'nomore';
uni.showToast({
title: '获取数据失败',
icon: 'none',
});
} finally {
if (!loadMore) {
this.loading = false;
}
}
},
// 加载更多数据
async loadMore() {
if (!this.hasMore || this.loadStatus !== 'loadmore') {
return;
}
this.loadStatus = 'loading';
this.queryParams.pageNum += 1;
try {
await this.getList(true);
} catch (error) {
console.error('加载更多数据失败:', error);
this.queryParams.pageNum -= 1; // 回退页码
this.loadStatus = 'loadmore';
}
},
// 查看按钮操作
handleView(row) {
this.$refs.disciplineRecordFormRef.openDialog(row.id, '查看');
},
// 撤销申请按钮操作
handleRevokeApply(row) {
if (row.status !== '1') {
uni.showToast({
title: '只有生效状态的违纪记录才能申请撤销',
icon: 'none',
});
return;
}
// 跳转到撤销申请页面
uni.navigateTo({
url: `/pages/xskh/wjjl/revokeApplication?disciplineRecordId=${row.id}`,
});
},
// 查看撤销申请按钮操作
handleViewRevoke(row) {
this.$refs.revokeApplicationViewRef.openDialog(row.id);
},
// 操作成功回调
handleSuccess() {
// 重新加载列表
this.queryParams.pageNum = 1;
this.hasMore = true;
this.loadStatus = 'loadmore';
this.getList();
},
// 格式化时间
formatTime(timeStr) {
if (!timeStr) return '-';
try {
const date = new Date(timeStr);
if (isNaN(date.getTime())) return timeStr;
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
} catch (error) {
console.error('时间格式化失败:', error);
return timeStr;
}
},
},
};
</script>
<style lang="scss" scoped>
.discipline-record-container {
background-color: #f5f6fa;
min-height: 100vh;
}
.content-wrapper {
padding: 20rpx;
}
.empty-container {
padding: 100rpx 20rpx;
}
.record-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.record-card {
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
overflow: hidden;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
border-bottom: 1px solid #f0f0f0;
}
.record-title {
font-size: 32rpx;
font-weight: bold;
color: #303133;
flex: 1;
}
.record-status {
margin-left: 16rpx;
}
.card-content {
padding: 24rpx;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
}
.label {
font-size: 28rpx;
color: #606266;
min-width: 140rpx;
margin-right: 16rpx;
}
.value {
font-size: 28rpx;
color: #303133;
flex: 1;
word-break: break-all;
}
.score-text {
color: #e6a23c;
font-weight: 500;
}
.card-actions {
display: flex;
justify-content: space-around;
padding: 20rpx;
border-top: 1px solid #f0f0f0;
gap: 16rpx;
}
.action-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 16rpx 20rpx;
border-radius: 12rpx;
// background-color: #f8f9fa;
min-width: 120rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
opacity: 0.8;
}
}
.btn-text {
font-size: 24rpx;
margin-top: 8rpx;
color: #303133;
font-weight: 500;
}
.info-btn {
// background-color: rgba(60, 156, 255, 0.1);
.btn-text {
color: #3c9cff;
}
}
.success-btn {
// background-color: rgba(90, 199, 37, 0.1);
.btn-text {
color: #5ac725;
}
}
.warning-btn {
// background-color: rgba(255, 153, 0, 0.1);
.btn-text {
color: #ff9900;
}
}
.load-more {
padding: 40rpx 20rpx;
}
// uview组件样式调整
::v-deep .u-tag {
margin: 0;
}
</style>