zlwu 1 rok temu
rodzic
commit
adb60d8dde

+ 8 - 0
mobile/src/pages.json

@@ -167,6 +167,14 @@
 				"navigationBarBackgroundColor": "#0051d9",
 				"navigationBarTextStyle": "white"
 			}
+		},
+		{
+			"path": "pages/xgss/sswj/sswjDetail",
+			"style": {
+				"navigationBarTitleText": "违纪详情",
+				"navigationBarBackgroundColor": "#0051d9",
+				"navigationBarTextStyle": "white"
+			}
 		}
 	],
 	"globalStyle": {

+ 13 - 5
mobile/src/pages/xgss/sswj/grsswj.vue

@@ -440,11 +440,19 @@ export default {
         handleView(item) {
             console.log('查看违纪记录详细:', item);
 
-            // 跳转到详情页面(后续开发)
-            uni.showToast({
-                title: '详情页面开发中',
-                icon: 'none',
-                duration: 2000,
+            // 跳转到详情页面并传递ID参数
+            uni.navigateTo({
+                url: `/pages/xgss/sswj/sswjDetail?id=${item.id}`,
+                success: () => {
+                    console.log('跳转到详情页面成功');
+                },
+                fail: (err) => {
+                    console.error('跳转到详情页面失败:', err);
+                    uni.showToast({
+                        title: '页面跳转失败',
+                        icon: 'none',
+                    });
+                },
             });
         },
     },

+ 1753 - 0
mobile/src/pages/xgss/sswj/sswjDetail.vue

@@ -0,0 +1,1753 @@
+<template>
+    <view class="violation-detail-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="!violationDetail" class="empty-container">
+                <u-empty text="未找到违纪记录" mode="data"></u-empty>
+            </view>
+
+            <!-- 违纪详情卡片 -->
+            <view v-else class="detail-card">
+                <!-- 基本信息 -->
+                <view class="section">
+                    <view class="section-title">
+                        <u-icon name="account" size="20" color="#3c9cff"></u-icon>
+                        <text class="title-text">学生信息</text>
+                    </view>
+                    <view class="info-grid">
+                        <view class="info-item">
+                            <text class="label">姓名:</text>
+                            <text class="value">{{ violationDetail.studentName || '-' }}</text>
+                        </view>
+                        <view class="info-item">
+                            <text class="label">学号:</text>
+                            <text class="value">{{ violationDetail.studentNo || '-' }}</text>
+                        </view>
+                        <view class="info-item">
+                            <text class="label">学院:</text>
+                            <text class="value">{{ violationDetail.collegeName || '-' }}</text>
+                        </view>
+                        <view class="info-item">
+                            <text class="label">专业:</text>
+                            <text class="value">{{ violationDetail.majorName || '-' }}</text>
+                        </view>
+                        <view class="info-item">
+                            <text class="label">班级:</text>
+                            <text class="value">{{ violationDetail.className || '-' }}</text>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 宿舍信息 -->
+                <view class="section">
+                    <view class="section-title">
+                        <u-icon name="home" size="20" color="#3c9cff"></u-icon>
+                        <text class="title-text">宿舍信息</text>
+                    </view>
+                    <view class="info-grid">
+                        <view class="info-item">
+                            <text class="label">校区:</text>
+                            <text class="value">{{ violationDetail.campusName || '-' }}</text>
+                        </view>
+                        <view class="info-item">
+                            <text class="label">宿舍楼:</text>
+                            <text class="value">{{ violationDetail.buildingName || '-' }}</text>
+                        </view>
+                        <view class="info-item">
+                            <text class="label">房间号:</text>
+                            <text class="value">{{ violationDetail.roomName || '-' }}</text>
+                        </view>
+                        <view class="info-item">
+                            <text class="label">床位:</text>
+                            <text class="value">{{ violationDetail.bedName || '-' }}</text>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 违纪信息 -->
+                <view class="section">
+                    <view class="section-title">
+                        <u-icon name="warning" size="20" color="#ff6b6b"></u-icon>
+                        <text class="title-text">违纪信息</text>
+                    </view>
+                    <view class="violation-info">
+                        <view class="info-row">
+                            <text class="label">违纪时间:</text>
+                            <text class="value">
+                                {{ formatTime(violationDetail.violationTime) }}
+                            </text>
+                        </view>
+                        <view class="info-row">
+                            <text class="label">违纪类型:</text>
+                            <view class="tag-container">
+                                <u-tag
+                                    :text="
+                                        getDictLabel(
+                                            dormViolationTypeDict,
+                                            violationDetail.violationType,
+                                        )
+                                    "
+                                    :type="
+                                        getDictTagType(
+                                            dormViolationTypeDict,
+                                            violationDetail.violationType,
+                                            violationTypeFallbackMap,
+                                        )
+                                    "
+                                    size="mini"
+                                ></u-tag>
+                            </view>
+                        </view>
+                        <view class="info-row">
+                            <text class="label">违纪等级:</text>
+                            <view class="tag-container">
+                                <u-tag
+                                    :text="
+                                        getDictLabel(
+                                            dormViolationLevelDict,
+                                            violationDetail.violationLevel,
+                                        )
+                                    "
+                                    :type="
+                                        getDictTagType(
+                                            dormViolationLevelDict,
+                                            violationDetail.violationLevel,
+                                            violationLevelFallbackMap,
+                                        )
+                                    "
+                                    size="mini"
+                                ></u-tag>
+                            </view>
+                        </view>
+                        <view class="info-row" v-if="violationDetail.violationDescription">
+                            <text class="label">违纪描述:</text>
+                            <text class="value description-text">
+                                {{ violationDetail.violationDescription }}
+                            </text>
+                        </view>
+                        <view class="info-row" v-if="violationDetail.remark">
+                            <text class="label">备注:</text>
+                            <text class="value description-text">{{ violationDetail.remark }}</text>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 处理信息 -->
+                <view class="section" v-if="violationDetail.processStatus">
+                    <view class="section-title">
+                        <u-icon name="checkmark-circle" size="20" color="#19be6b"></u-icon>
+                        <text class="title-text">处理信息</text>
+                    </view>
+                    <view class="info-grid">
+                        <view class="info-item">
+                            <text class="label">处理状态:</text>
+                            <u-tag
+                                :text="getProcessStatusText(violationDetail.processStatus)"
+                                :type="getProcessStatusType(violationDetail.processStatus)"
+                                size="mini"
+                            ></u-tag>
+                        </view>
+                        <view class="info-item" v-if="violationDetail.processTime">
+                            <text class="label">处理时间:</text>
+                            <text class="value">{{ formatTime(violationDetail.processTime) }}</text>
+                        </view>
+                        <view class="info-item" v-if="violationDetail.processBy">
+                            <text class="label">处理人:</text>
+                            <text class="value">{{ violationDetail.processBy }}</text>
+                        </view>
+                        <view class="info-item" v-if="violationDetail.processResult">
+                            <text class="label">处理结果:</text>
+                            <text class="value">{{ violationDetail.processResult }}</text>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 涉及其他学生 -->
+                <view class="section" v-if="involvedStudents.length > 0">
+                    <view class="section-title">
+                        <u-icon name="account-fill" size="20" color="#ff9500"></u-icon>
+                        <text class="title-text">涉及其他学生</text>
+                        <text class="student-count">{{ involvedStudentsCount }}</text>
+                    </view>
+                    <view class="involved-students">
+                        <view
+                            v-for="(student, index) in involvedStudents"
+                            :key="student.id || index"
+                            class="student-card"
+                        >
+                            <view class="student-header">
+                                <!-- <view class="student-avatar">
+                                    <u-icon
+                                        :name="getGenderIcon(student.gender)"
+                                        size="24"
+                                        :color="getGenderColor(student.gender)"
+                                    ></u-icon>
+                                </view> -->
+                                <view class="student-basic">
+                                    <text class="student-name">{{ student.name || '-' }}</text>
+                                    <text class="student-no">{{ student.studentNo || '-' }}</text>
+                                </view>
+                                <view class="student-gender">
+                                    <u-tag
+                                        :text="getGenderText(student.gender)"
+                                        :type="getGenderType(student.gender)"
+                                        size="mini"
+                                    ></u-tag>
+                                </view>
+                            </view>
+                            <view class="student-details">
+                                <view class="detail-row">
+                                    <text class="detail-label">学院:</text>
+                                    <text class="detail-value">
+                                        {{ student.collegeName || '-' }}
+                                    </text>
+                                </view>
+                                <view class="detail-row">
+                                    <text class="detail-label">专业:</text>
+                                    <text class="detail-value">{{ student.majorName || '-' }}</text>
+                                </view>
+                                <view class="detail-row">
+                                    <text class="detail-label">班级:</text>
+                                    <text class="detail-value">{{ student.className || '-' }}</text>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 涉及其他学生(加载状态) -->
+                <view class="section" v-else-if="involvedStudentsLoading">
+                    <view class="section-title">
+                        <u-icon name="account-fill" size="20" color="#ff9500"></u-icon>
+                        <text class="title-text">涉及其他学生</text>
+                    </view>
+                    <view class="loading-involved-students">
+                        <u-loading-icon mode="spinner" size="28"></u-loading-icon>
+                        <text class="loading-text">正在加载涉及学生信息...</text>
+                    </view>
+                </view>
+
+                <!-- 涉及其他学生(空状态) -->
+                <view class="section" v-else-if="showInvolvedStudents">
+                    <view class="section-title">
+                        <u-icon name="account-fill" size="20" color="#ff9500"></u-icon>
+                        <text class="title-text">涉及其他学生</text>
+                    </view>
+                    <view class="empty-involved-students">
+                        <u-empty text="暂无涉及其他学生信息" mode="data"></u-empty>
+                    </view>
+                </view>
+
+                <!-- 涉及其他学生(错误状态) -->
+                <view class="section" v-else-if="involvedStudentsError">
+                    <view class="section-title">
+                        <u-icon name="account-fill" size="20" color="#ff9500"></u-icon>
+                        <text class="title-text">涉及其他学生</text>
+                    </view>
+                    <view class="error-involved-students">
+                        <u-empty text="获取涉及学生信息失败" mode="error"></u-empty>
+                        <view class="error-actions">
+                            <u-button size="mini" type="primary" @click="retryGetInvolvedStudents">
+                                重试
+                            </u-button>
+                        </view>
+                    </view>
+                </view>
+                <!-- 附件信息 -->
+                <view class="section" v-if="attachmentList.length > 0">
+                    <view class="section-title">
+                        <u-icon name="folder" size="20" color="#ff9500"></u-icon>
+                        <text class="title-text">违纪附件</text>
+                    </view>
+                    <view class="attachment-area">
+                        <!-- 图片展示区域 -->
+                        <view v-if="imageFiles.length > 0" class="image-section">
+                            <view class="image-title">图片附件</view>
+                            <view class="image-grid">
+                                <view
+                                    v-for="image in imageFiles"
+                                    :key="image.uid"
+                                    class="image-item"
+                                    @click="handleImagePreview(image)"
+                                >
+                                    <!-- 图片容器 -->
+                                    <view class="image-container">
+                                        <image
+                                            :src="image.blobUrl || image.url"
+                                            class="attachment-image"
+                                            mode="aspectFill"
+                                        ></image>
+
+                                        <!-- 成功状态角标 -->
+                                        <view class="success-badge">
+                                            <u-icon
+                                                name="checkmark-circle-fill"
+                                                size="18"
+                                                color="#52c41a"
+                                            ></u-icon>
+                                        </view>
+                                    </view>
+
+                                    <!-- 图片操作按钮 -->
+                                    <view class="image-actions" @click.stop>
+                                        <u-button
+                                            size="mini"
+                                            type="primary"
+                                            plain
+                                            @tap="handleImageDownload(image)"
+                                            @click.stop.prevent="
+                                                handleImageDownloadClick(image, $event)
+                                            "
+                                        >
+                                            下载
+                                        </u-button>
+                                    </view>
+
+                                    <!-- 文件名显示 -->
+                                    <view class="image-name">
+                                        <text>{{ getShortFileName(image.name) }}</text>
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+
+                        <!-- 自定义文件列表(用于显示非图片文件) -->
+                        <view class="custom-file-list" v-if="nonImageFiles.length > 0">
+                            <view class="file-title">文档附件</view>
+                            <view class="file-grid">
+                                <view
+                                    class="file-card"
+                                    v-for="file in nonImageFiles"
+                                    :key="file.uid"
+                                >
+                                    <!-- 文件图标容器 -->
+                                    <view class="file-icon-container">
+                                        <u-icon name="folder" size="50" color="#19be6b" />
+
+                                        <!-- 成功状态角标 -->
+                                        <view class="success-badge">
+                                            <u-icon
+                                                name="checkmark-circle-fill"
+                                                size="18"
+                                                color="#52c41a"
+                                            ></u-icon>
+                                        </view>
+                                    </view>
+
+                                    <!-- 文件操作按钮 -->
+                                    <view class="image-actions" @click.stop>
+                                        <u-button
+                                            size="mini"
+                                            type="primary"
+                                            plain
+                                            @tap="handleDownloadFile(file)"
+                                            @click.stop.prevent="
+                                                handleDownloadFileClick(file, $event)
+                                            "
+                                        >
+                                            下载
+                                        </u-button>
+                                    </view>
+
+                                    <!-- 文件名显示 -->
+                                    <view class="file-name-display">
+                                        <text>{{ getShortFileName(file.name) }}</text>
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+import { request, downloadFile } from '@/common/request.js';
+import { getDict, getDictLabel, getDictTagType } from '@/common/dict.js';
+
+export default {
+    name: 'ViolationDetail',
+    data() {
+        return {
+            loading: false,
+            violationDetail: null,
+            violationId: null,
+
+            // 字典数据
+            dormViolationTypeDict: [],
+            dormViolationLevelDict: [],
+
+            // 附件列表
+            attachmentList: [],
+
+            // 涉及其他学生列表
+            involvedStudents: [],
+
+            // 涉及学生信息加载状态
+            involvedStudentsLoading: false,
+
+            // 涉及学生信息错误状态
+            involvedStudentsError: false,
+
+            // 学生信息缓存
+            studentCache: new Map(),
+
+            // 字典标签类型降级映射
+            violationTypeFallbackMap: {
+                WS001001: 'warning', // 垃圾乱扔
+                WS001002: 'warning', // 床铺不整
+                WS001003: 'warning', // 物品摆放混乱
+                JL001001: 'info', // 晚归违纪
+                JL001002: 'info', // 噪音扰民
+                JL001003: 'error', // 夜不归宿
+                AQ001001: 'error', // 违规用电
+                AQ001002: 'error', // 吸烟违纪
+                AQ001003: 'error', // 明火使用
+                WP001001: 'primary', // 损坏公物
+                WP001002: 'primary', // 私改设施
+                WP001003: 'error', // 盗用他人物品
+            },
+
+            violationLevelFallbackMap: {
+                1: 'success', // 轻微
+                2: 'warning', // 一般
+                3: 'error', // 严重
+                4: 'error', // 特别严重
+            },
+        };
+    },
+
+    computed: {
+        // 图片文件(用于内部处理)
+        imageFiles() {
+            return this.attachmentList.filter((file) => this.isImageFile(file));
+        },
+
+        // 非图片文件(自定义显示)
+        nonImageFiles() {
+            return this.attachmentList.filter((file) => !this.isImageFile(file));
+        },
+
+        // 是否显示涉及学生卡片
+        showInvolvedStudents() {
+            return this.violationDetail && this.violationDetail.involvedStudents;
+        },
+
+        // 涉及学生数量信息
+        involvedStudentsCount() {
+            if (!this.showInvolvedStudents) return '';
+            return `(${this.involvedStudents.length}人)`;
+        },
+    },
+
+    onLoad(options) {
+        console.log('详情页面参数:', options);
+        if (options.id) {
+            this.violationId = options.id;
+            this.initData();
+        } else {
+            uni.showToast({
+                title: '参数错误',
+                icon: 'none',
+            });
+            setTimeout(() => {
+                this.goBack();
+            }, 1500);
+        }
+    },
+
+    onUnload() {
+        // 清理blob URL,防止内存泄漏
+        this.cleanupBlobUrls();
+    },
+
+    methods: {
+        // 返回上一页
+        goBack() {
+            uni.navigateBack();
+        },
+
+        // 初始化数据
+        async initData() {
+            await Promise.all([this.loadDictData(), this.getDetail()]);
+        },
+
+        // 加载字典数据
+        async loadDictData() {
+            try {
+                const [typeDict, levelDict] = await Promise.all([
+                    getDict('dorm_violation_type'),
+                    getDict('dorm_violation_level'),
+                ]);
+
+                this.dormViolationTypeDict = typeDict;
+                this.dormViolationLevelDict = levelDict;
+
+                console.log('字典数据加载完成:', {
+                    type: this.dormViolationTypeDict.length,
+                    level: this.dormViolationLevelDict.length,
+                });
+            } catch (error) {
+                console.error('加载字典数据失败:', error);
+            }
+        },
+
+        // 获取字典标签
+        getDictLabel(dictData, value) {
+            return getDictLabel(dictData, value);
+        },
+
+        // 获取字典标签类型
+        getDictTagType(dictData, value, fallbackMap) {
+            return getDictTagType(dictData, value, fallbackMap);
+        },
+
+        // 获取违纪详情
+        async getDetail() {
+            if (!this.violationId) {
+                uni.showToast({
+                    title: '参数错误',
+                    icon: 'none',
+                });
+                return;
+            }
+
+            this.loading = true;
+
+            // 清理之前的数据
+            this.involvedStudents = [];
+            this.attachmentList = [];
+            this.involvedStudentsLoading = false;
+            this.involvedStudentsError = false; // 重置错误状态
+            this.studentCache.clear(); // 清理学生信息缓存
+
+            try {
+                const res = await request({
+                    url: `/dormViolation/dormViolation/${this.violationId}`,
+                    method: 'GET',
+                });
+
+                if (res && res.code === 200) {
+                    this.violationDetail = res.data;
+                    console.log('获取违纪详情成功:', this.violationDetail);
+
+                    // 获取附件信息
+                    if (this.violationDetail.attachmentOssIds) {
+                        await this.getAttachmentFiles(this.violationDetail.attachmentOssIds);
+                    }
+
+                    // 获取涉及其他学生信息
+                    if (this.violationDetail.involvedStudents) {
+                        await this.getInvolvedStudents(this.violationDetail.involvedStudents);
+                    }
+                } else {
+                    uni.showToast({
+                        title: res?.msg || '获取详情失败',
+                        icon: 'none',
+                    });
+                }
+            } catch (error) {
+                console.error('获取违纪详情失败:', error);
+                uni.showToast({
+                    title: '获取详情失败',
+                    icon: 'none',
+                });
+                this.involvedStudentsError = true; // 设置错误状态
+            } finally {
+                this.loading = false;
+            }
+        },
+
+        // 获取附件文件
+        async getAttachmentFiles(ossIds) {
+            try {
+                const response = await request({
+                    url: `/resource/oss/listByIds/${ossIds}`,
+                    method: 'GET',
+                });
+
+                if (response.code === 200 && response.data) {
+                    const attachmentFiles = response.data.map((item, index) => {
+                        // 使用fileSuffix字段判断文件类型
+                        const fileType = this.getFileTypeBySuffix(item.fileSuffix);
+                        const isImage = this.isImageFile({
+                            name: item.originalName,
+                            type: fileType,
+                        });
+
+                        // 处理图片URL - 确保有正确的API前缀
+                        let processedUrl = item.url;
+                        if (isImage && item.url && item.url.startsWith('/resource/')) {
+                            // 图片文件需要添加/api前缀
+                            processedUrl = `/api${item.url}`;
+                        }
+
+                        // 构造完整的文件名(包含扩展名)
+                        let fullFileName = item.originalName || `附件${index + 1}`;
+                        if (
+                            item.fileSuffix &&
+                            !fullFileName
+                                .toLowerCase()
+                                .endsWith(`.${item.fileSuffix.toLowerCase()}`)
+                        ) {
+                            fullFileName = `${fullFileName}.${item.fileSuffix}`;
+                        }
+
+                        const fileObj = {
+                            uid: `existing_${index}_${Date.now()}`,
+                            name: fullFileName,
+                            url: processedUrl,
+                            ossId: item.ossId,
+                            status: 'success',
+                            type: fileType,
+                            // 确保图片文件有正确的显示属性
+                            size: item.fileSize || 0,
+                            // 为uview2 upload组件添加必要的属性
+                            file: {
+                                name: fullFileName,
+                                type: fileType,
+                                size: item.fileSize || 0,
+                                path: processedUrl,
+                                url: processedUrl,
+                            },
+                        };
+
+                        return fileObj;
+                    });
+
+                    this.attachmentList = attachmentFiles;
+
+                    // 为图片文件获取blob数据以确保正确显示
+                    await this.processImageFiles();
+
+                    // 强制刷新组件显示
+                    this.$nextTick(() => {
+                        // 预加载图片以确保能正确显示
+                        this.preloadImages();
+                    });
+                } else {
+                    console.warn('获取附件信息失败:', response.msg);
+                    this.attachmentList = [];
+                }
+            } catch (error) {
+                console.error('获取附件失败:', error);
+                this.attachmentList = [];
+            }
+        },
+
+        // 获取涉及其他学生信息
+        async getInvolvedStudents(involvedStudentsStr) {
+            if (!involvedStudentsStr) {
+                this.involvedStudents = [];
+                return;
+            }
+
+            this.involvedStudentsLoading = true;
+            this.involvedStudentsError = false;
+
+            try {
+                // 解析学生ID字符串(逗号分隔)
+                const studentIds = involvedStudentsStr
+                    .split(',')
+                    .map((id) => id.trim())
+                    .filter((id) => id && !isNaN(Number(id))); // 过滤掉空值和非数字ID
+
+                // 去重
+                const uniqueStudentIds = [...new Set(studentIds)];
+
+                if (uniqueStudentIds.length !== studentIds.length) {
+                    console.log(`学生ID去重: ${studentIds.length} -> ${uniqueStudentIds.length}`);
+                }
+
+                if (uniqueStudentIds.length === 0) {
+                    this.involvedStudents = [];
+                    return;
+                }
+
+                // 限制最多获取10个学生信息,防止性能问题
+                const maxStudents = 10;
+                if (uniqueStudentIds.length > maxStudents) {
+                    console.warn(
+                        `涉及学生数量过多(${uniqueStudentIds.length}),只获取前${maxStudents}个`,
+                    );
+                    uniqueStudentIds.splice(maxStudents);
+                }
+
+                console.log('涉及学生ID列表:', uniqueStudentIds);
+
+                // 并发获取所有学生的详细信息
+                const studentPromises = uniqueStudentIds.map(async (studentId) => {
+                    try {
+                        // 检查缓存
+                        if (this.studentCache.has(studentId)) {
+                            console.log(`从缓存获取学生${studentId}信息`);
+                            return this.studentCache.get(studentId);
+                        }
+
+                        const response = await request({
+                            url: `/dormViolation/dormViolation/student/${studentId}`,
+                            method: 'GET',
+                        });
+
+                        if (response.code === 200 && response.data) {
+                            console.log(`获取学生${studentId}信息成功:`, response.data);
+                            // 缓存学生信息
+                            this.studentCache.set(studentId, response.data);
+                            return response.data;
+                        } else {
+                            console.warn(`获取学生${studentId}信息失败:`, response.msg);
+                            return null;
+                        }
+                    } catch (error) {
+                        console.error(`获取学生${studentId}信息异常:`, error);
+                        return null;
+                    }
+                });
+
+                // 等待所有请求完成
+                const students = await Promise.all(studentPromises);
+
+                // 过滤掉获取失败的学生信息,并验证必要字段
+                this.involvedStudents = students.filter((student) => {
+                    if (!student) return false;
+
+                    // 验证必要字段
+                    const hasRequiredFields = student.id && student.name && student.studentNo;
+                    if (!hasRequiredFields) {
+                        console.warn('学生信息缺少必要字段:', student);
+                    }
+
+                    return hasRequiredFields;
+                });
+
+                // 按学号排序
+                this.involvedStudents.sort((a, b) => {
+                    return (a.studentNo || '').localeCompare(b.studentNo || '');
+                });
+
+                console.log('涉及学生信息获取完成:', this.involvedStudents);
+
+                // 如果有学生ID但获取失败,显示提示
+                if (studentIds.length > 0 && this.involvedStudents.length === 0) {
+                    console.warn('所有涉及学生信息获取失败');
+                }
+            } catch (error) {
+                console.error('获取涉及学生信息失败:', error);
+                this.involvedStudents = [];
+                this.involvedStudentsError = true; // 设置错误状态
+            } finally {
+                this.involvedStudentsLoading = false;
+            }
+        },
+
+        // 重试获取涉及学生信息
+        async retryGetInvolvedStudents() {
+            this.involvedStudentsLoading = true;
+            this.involvedStudentsError = false;
+            await this.getInvolvedStudents(this.violationDetail.involvedStudents);
+        },
+
+        // 根据文件后缀获取文件类型
+        getFileTypeBySuffix(fileSuffix) {
+            if (!fileSuffix) return '';
+
+            // 移除点号,转换为小写
+            const ext = fileSuffix.replace('.', '').toLowerCase();
+
+            // 图片类型判断
+            const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
+            if (imageTypes.includes(ext)) {
+                // 修正MIME类型映射
+                const mimeTypeMap = {
+                    jpg: 'image/jpeg',
+                    jpeg: 'image/jpeg',
+                    png: 'image/png',
+                    gif: 'image/gif',
+                    bmp: 'image/bmp',
+                    webp: 'image/webp',
+                };
+                return mimeTypeMap[ext] || `image/${ext}`;
+            }
+
+            // 文档类型判断
+            const documentTypes = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'];
+            if (documentTypes.includes(ext)) {
+                return `file/${ext}`;
+            }
+
+            // 其他类型
+            return `file/${ext}`;
+        },
+
+        // 文件类型判断
+        isImageFile(file) {
+            const imageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'];
+
+            // 优先使用文件MIME类型判断
+            if (file.type) {
+                return imageTypes.includes(file.type);
+            }
+
+            // 通过文件名判断
+            const fileName = file.name || file.url || '';
+            return /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(fileName);
+        },
+
+        // 获取缩写的文件名(保留后缀)
+        getShortFileName(fileName) {
+            if (!fileName) return '';
+
+            const lastDotIndex = fileName.lastIndexOf('.');
+            if (lastDotIndex === -1) {
+                // 没有后缀的文件名
+                return fileName.length > 12 ? fileName.substring(0, 12) + '...' : fileName;
+            }
+
+            const name = fileName.substring(0, lastDotIndex);
+            const ext = fileName.substring(lastDotIndex); // 包含点号的后缀
+
+            if (name.length <= 8) {
+                return fileName; // 文件名较短,直接返回
+            }
+
+            // 文件名较长,缩写中间部分,保留后缀
+            return name.substring(0, 8) + '...' + ext;
+        },
+
+        // 图片预览
+        handleImagePreview(image) {
+            if (image.status !== 'success') {
+                uni.showToast({
+                    title: '图片尚未加载完成',
+                    icon: 'none',
+                });
+                return;
+            }
+
+            // 获取所有成功上传的图片URL
+            const imageUrls = this.imageFiles
+                .filter((file) => file.status === 'success')
+                .map((file) => file.blobUrl || file.url);
+
+            if (imageUrls.length === 0) {
+                uni.showToast({
+                    title: '暂无可预览的图片',
+                    icon: 'none',
+                });
+                return;
+            }
+
+            // 找到当前图片在成功列表中的索引
+            const currentUrl = image.blobUrl || image.url;
+            const currentIndex = imageUrls.findIndex((url) => url === currentUrl);
+
+            uni.previewImage({
+                urls: imageUrls,
+                current: currentIndex >= 0 ? currentIndex : 0,
+                success: () => {
+                    // 预览成功
+                },
+                fail: (error) => {
+                    console.error('图片预览失败:', error);
+                    uni.showToast({
+                        title: '预览失败',
+                        icon: 'none',
+                    });
+                },
+            });
+        },
+
+        // 图片下载按钮点击处理(更强的事件处理)
+        handleImageDownloadClick(image, event) {
+            // 强制阻止事件冒泡和默认行为
+            if (event) {
+                event.stopPropagation();
+                event.preventDefault();
+                event.stopImmediatePropagation();
+            }
+
+            // 调用实际的下载方法
+            this.handleImageDownload(image, event);
+        },
+
+        // 图片下载(带重试机制)
+        async handleImageDownload(image, event, retryCount = 0) {
+            // 事件阻止已由handleImageDownloadClick方法处理
+
+            if (image.status !== 'success') {
+                uni.showToast({
+                    title: '图片尚未加载完成',
+                    icon: 'none',
+                });
+                return;
+            }
+
+            // 显示下载进度提示
+            uni.showLoading({
+                title: '正在下载图片...',
+                mask: true,
+            });
+
+            try {
+                // 如果有ossId,通过API下载
+                if (image.ossId) {
+                    const downloadUrl = `/resource/oss/download/${image.ossId}`;
+
+                    // 调用下载接口并等待完成
+                    const response = await downloadFile(downloadUrl, image.name);
+
+                    if (response.statusCode === 200) {
+                        uni.hideLoading();
+                        uni.saveFile({
+                            tempFilePath: response.tempFilePath,
+                            success: function (res) {
+                                uni.showToast({
+                                    title: '下载成功',
+                                    icon: 'success',
+                                });
+                            },
+                            fail: function () {
+                                uni.showToast({
+                                    title: '保存失败',
+                                    icon: 'none',
+                                });
+                            },
+                        });
+                    }
+                }
+            } catch (error) {
+                console.error('图片下载过程出错:', error);
+
+                // 确保隐藏loading状态
+                uni.hideLoading();
+
+                // 重试机制(最多重试2次)
+                if (retryCount < 2) {
+                    // 显示重试提示
+                    uni.showToast({
+                        title: `下载失败,正在重试(${retryCount + 1}/2)...`,
+                        icon: 'none',
+                        duration: 1500,
+                    });
+
+                    setTimeout(() => {
+                        this.handleImageDownload(image, event, retryCount + 1);
+                    }, 1500); // 延迟1.5秒后重试
+
+                    return;
+                }
+
+                // 超过重试次数,显示最终错误信息
+                const errorMsg = error.message?.includes('网络')
+                    ? '网络连接失败,请检查网络后重试'
+                    : error.message?.includes('timeout')
+                    ? '下载超时,请重试'
+                    : '下载失败,已重试2次,请检查网络或稍后重试';
+
+                uni.showToast({
+                    title: errorMsg,
+                    icon: 'none',
+                    duration: 3000,
+                });
+            }
+        },
+
+        // 文件下载按钮点击处理(更强的事件处理)
+        handleDownloadFileClick(file, event) {
+            // 强制阻止事件冒泡和默认行为
+            if (event) {
+                event.stopPropagation();
+                event.preventDefault();
+                event.stopImmediatePropagation();
+            }
+
+            // 调用实际的下载方法
+            this.handleDownloadFile(file, event);
+        },
+
+        // 文件下载(微信小程序专用版本)
+        async handleDownloadFile(file, event) {
+            // 事件阻止已由handleDownloadFileClick方法处理
+
+            if (!file.ossId) {
+                uni.showToast({
+                    title: '文件未加载完成',
+                    icon: 'none',
+                });
+                return;
+            }
+
+            try {
+                uni.showLoading({
+                    title: `正在下载${file.name}...`,
+                    mask: true,
+                });
+
+                // 构建下载URL
+                const downloadUrl = `/resource/oss/download/${file.ossId}`;
+
+                if (this.isImageFile(file)) {
+                    // 图片文件:下载并保存到相册
+                    await this.downloadAndSaveImage(downloadUrl, file.name);
+                } else {
+                    // 非图片文件:下载并直接打开预览
+                    await this.downloadAndOpenDocument(downloadUrl, file.name);
+                }
+            } catch (error) {
+                console.error('下载过程发生异常:', error);
+                uni.hideLoading();
+
+                let errorMsg = '下载失败,请重试';
+                if (error.message.includes('timeout')) {
+                    errorMsg = '下载超时,请检查网络后重试';
+                } else if (error.message.includes('network')) {
+                    errorMsg = '网络错误,请检查网络连接';
+                } else if (error.message.includes('404')) {
+                    errorMsg = '文件不存在,请联系管理员';
+                } else if (error.message.includes('500')) {
+                    errorMsg = '服务器错误,请稍后重试';
+                }
+
+                uni.showModal({
+                    title: '下载失败',
+                    content: errorMsg,
+                    showCancel: true,
+                    cancelText: '取消',
+                    confirmText: '重试',
+                    success: (res) => {
+                        if (res.confirm) {
+                            this.handleDownloadFile(file);
+                        }
+                    },
+                });
+            }
+        },
+
+        // 下载图片并保存到相册
+        async downloadAndSaveImage(downloadUrl, fileName) {
+            return new Promise(async (resolve, reject) => {
+                // 调用下载接口并等待完成
+                const response = await downloadFile(downloadUrl, fileName);
+
+                if (response.statusCode === 200) {
+                    uni.hideLoading();
+                    uni.saveFile({
+                        tempFilePath: response.tempFilePath,
+                        success: function (res) {
+                            uni.showToast({
+                                title: '下载成功',
+                                icon: 'success',
+                            });
+                        },
+                        fail: function () {
+                            uni.showToast({
+                                title: '保存失败',
+                                icon: 'none',
+                            });
+                        },
+                    });
+                }
+                uni.hideLoading();
+                resolve();
+            });
+        },
+
+        // 下载文档并打开预览
+        async downloadAndOpenDocument(downloadUrl, fileName) {
+            return new Promise(async (resolve, reject) => {
+                // 调用下载接口并等待完成
+                const response = await downloadFile(downloadUrl, fileName);
+
+                if (response.statusCode === 200) {
+                    uni.hideLoading();
+                    uni.saveFile({
+                        tempFilePath: response.tempFilePath,
+                        success: function (res) {
+                            uni.showToast({
+                                title: '下载成功',
+                                icon: 'success',
+                            });
+                        },
+                        fail: function () {
+                            uni.showToast({
+                                title: '保存失败',
+                                icon: 'none',
+                            });
+                        },
+                    });
+                }
+                uni.hideLoading();
+                resolve();
+            });
+        },
+
+        // 预加载图片以确保能正确显示
+        async preloadImages() {
+            console.log('=== 预加载图片 ===');
+            const imageFiles = this.attachmentList.filter((file) => this.isImageFile(file));
+
+            for (const file of imageFiles) {
+                if (file.url) {
+                    try {
+                        console.log(`预加载图片: ${file.name} - ${file.url}`);
+                        // 创建一个Image对象来预加载图片
+                        const img = new Image();
+                        img.onload = () => {
+                            console.log(`图片预加载成功: ${file.name}`);
+                        };
+                        img.onerror = () => {
+                            console.error(`图片预加载失败: ${file.name} - ${file.url}`);
+                        };
+                        img.src = file.url;
+                    } catch (error) {
+                        console.error(`预加载图片出错: ${file.name}`, error);
+                    }
+                }
+            }
+        },
+
+        // 获取图片的blob数据(用于uview2 upload组件显示)
+        async getImageBlobUrl(imageUrl) {
+            try {
+                console.log(`获取图片blob数据: ${imageUrl}`);
+
+                // 处理图片URL路径
+                let apiUrl = imageUrl;
+                if (imageUrl.startsWith('/api/')) {
+                    // 移除/api前缀,因为request会自动添加baseURL
+                    apiUrl = imageUrl.replace('/api', '');
+                } else if (imageUrl.startsWith('/resource/')) {
+                    // 直接使用相对路径
+                    apiUrl = imageUrl;
+                } else if (imageUrl.startsWith('http')) {
+                    // 完整URL,需要特殊处理
+                    console.log('检测到完整URL,直接返回原URL');
+                    return imageUrl;
+                }
+
+                console.log(`API请求URL: ${apiUrl}`);
+
+                // 使用request方法获取图片数据
+                const response = await request({
+                    url: apiUrl,
+                    method: 'GET',
+                    responseType: 'arraybuffer', // 设置响应类型为arraybuffer
+                    header: {
+                        isToken: true, // 图片请求可能不需要token验证
+                    },
+                });
+
+                // 将arraybuffer转换为blob
+                const blob = new Blob([response], { type: 'image/*' });
+                const blobUrl = URL.createObjectURL(blob);
+
+                console.log(`成功创建blob URL: ${blobUrl}`);
+                return blobUrl;
+            } catch (error) {
+                console.error(`获取图片blob数据失败: ${imageUrl}`, error);
+                // 如果请求失败,返回原始URL作为备用方案
+                return imageUrl;
+            }
+        },
+
+        // 处理图片文件,获取blob数据以确保正确显示
+        async processImageFiles() {
+            console.log('=== 处理图片文件 ===');
+            const imageFiles = this.attachmentList.filter((file) => this.isImageFile(file));
+
+            for (const file of imageFiles) {
+                if (file.url && !file.blobUrl) {
+                    try {
+                        console.log(`处理图片文件: ${file.name}`);
+                        const blobUrl = await this.getImageBlobUrl(file.url);
+                        if (blobUrl) {
+                            // 更新文件对象,添加blob URL,并标记为图片
+                            file.blobUrl = blobUrl;
+                            file.url = blobUrl; // 使用blob URL作为显示URL
+                            file.fileType = 'image';
+                            file.type =
+                                file.type && file.type.startsWith('image') ? file.type : 'image';
+                            file.isImage = true;
+                            console.log(`图片文件处理成功: ${file.name} - ${blobUrl}`);
+                        }
+                    } catch (error) {
+                        console.error(`处理图片文件失败: ${file.name}`, error);
+                    }
+                }
+            }
+        },
+
+        // 清理blob URL,防止内存泄漏
+        cleanupBlobUrls() {
+            console.log('=== 清理blob URL ===');
+            this.attachmentList.forEach((file) => {
+                if (file.blobUrl && file.blobUrl.startsWith('blob:')) {
+                    try {
+                        URL.revokeObjectURL(file.blobUrl);
+                        console.log(`已清理blob URL: ${file.name}`);
+                    } catch (error) {
+                        console.error(`清理blob URL失败: ${file.name}`, error);
+                    }
+                }
+            });
+
+            // 清理涉及学生数据
+            this.involvedStudents = [];
+            this.involvedStudentsLoading = false;
+            this.involvedStudentsError = false; // 重置错误状态
+            this.studentCache.clear(); // 清理学生信息缓存
+            console.log('已清理涉及学生数据');
+        },
+
+        // 格式化时间显示
+        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;
+            }
+        },
+
+        // 获取处理状态文本
+        getProcessStatusText(status) {
+            const statusMap = {
+                0: '待处理',
+                1: '处理中',
+                2: '已处理',
+                3: '已撤销',
+            };
+            return statusMap[status] || '未知状态';
+        },
+
+        // 获取处理状态类型
+        getProcessStatusType(status) {
+            const typeMap = {
+                0: 'warning',
+                1: 'primary',
+                2: 'success',
+                3: 'info',
+            };
+            return typeMap[status] || 'info';
+        },
+
+        // 获取性别文本
+        getGenderText(gender) {
+            if (gender === '1') {
+                return '女';
+            } else if (gender === '0') {
+                return '男';
+            } else {
+                return '未知';
+            }
+        },
+
+        // 获取性别类型
+        getGenderType(gender) {
+            if (gender === '1') {
+                return 'warning'; // 粉色
+            } else if (gender === '0') {
+                return 'primary'; // 蓝色
+            } else {
+                return 'info'; // 灰色
+            }
+        },
+
+        // 获取性别图标
+        getGenderIcon(gender) {
+            if (gender === '1') {
+                return 'woman';
+            } else if (gender === '0') {
+                return 'man';
+            } else {
+                return 'question';
+            }
+        },
+
+        // 获取性别颜色
+        getGenderColor(gender) {
+            if (gender === '1') {
+                return '#ff69b4'; // 粉色
+            } else if (gender === '0') {
+                return '#3c9cff'; // 蓝色
+            } else {
+                return '#909399'; // 灰色
+            }
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.violation-detail-container {
+    background-color: #f5f6fa;
+    min-height: 100vh;
+}
+
+.content-wrapper {
+    padding: 20rpx;
+}
+
+.empty-container {
+    padding: 100rpx 20rpx;
+}
+
+.detail-card {
+    background-color: #fff;
+    border-radius: 16rpx;
+    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
+    overflow: hidden;
+}
+
+.section {
+    padding: 24rpx;
+    border-bottom: 1px solid #f0f0f0;
+
+    &:last-child {
+        border-bottom: none;
+    }
+}
+
+.section-title {
+    display: flex;
+    align-items: center;
+    margin-bottom: 20rpx;
+    gap: 12rpx;
+}
+
+.title-text {
+    font-size: 32rpx;
+    font-weight: bold;
+    color: #303133;
+}
+
+.student-count {
+    font-size: 28rpx;
+    color: #909399;
+    margin-left: 8rpx;
+}
+
+.info-grid {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.info-item {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+}
+
+.info-row {
+    display: flex;
+    align-items: flex-start;
+    justify-content: space-between;
+    margin-bottom: 16rpx;
+
+    &:last-child {
+        margin-bottom: 0;
+    }
+}
+
+.label {
+    font-size: 28rpx;
+    color: #606266;
+    min-width: 120rpx;
+    flex-shrink: 0;
+}
+
+.value {
+    font-size: 28rpx;
+    color: #303133;
+    flex: 1;
+    word-break: break-all;
+    text-align: right;
+}
+
+.description-text {
+    line-height: 1.5;
+}
+
+.tag-container {
+    flex: 1;
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+}
+
+// 附件区域样式
+.attachment-area {
+    padding: 20rpx 0;
+}
+
+// 图片展示区域样式
+.image-section {
+    margin-bottom: 30rpx;
+}
+
+.image-title {
+    font-size: 28rpx;
+    font-weight: 600;
+    color: #333;
+    margin-bottom: 20rpx;
+    padding-left: 10rpx;
+    border-left: 4rpx solid #2979ff;
+}
+
+.image-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(280rpx, 1fr));
+    gap: 20rpx;
+}
+
+.image-item {
+    position: relative;
+    background: #fff;
+    border-radius: 12rpx;
+    overflow: hidden;
+    border: 2rpx solid #f0f0f0;
+    transition: all 0.3s ease;
+    cursor: pointer;
+}
+
+.image-item:active {
+    transform: scale(0.98);
+    border-color: #2979ff;
+}
+
+.image-container {
+    position: relative;
+    width: 100%;
+    height: 200rpx;
+    border-radius: 8rpx;
+    overflow: hidden;
+    cursor: pointer;
+}
+
+.attachment-image {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+    background-color: #f5f5f5;
+}
+
+.success-badge {
+    position: absolute;
+    top: 8rpx;
+    right: 8rpx;
+    background-color: rgba(255, 255, 255, 0.9);
+    border-radius: 50%;
+    padding: 4rpx;
+    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+}
+
+.image-actions {
+    display: flex;
+    gap: 6rpx;
+    padding: 8rpx 12rpx;
+    background: #fafafa;
+    border-top: 1rpx solid #f0f0f0;
+    flex-wrap: nowrap;
+    justify-content: space-between;
+    position: relative;
+    z-index: 10;
+    pointer-events: auto;
+}
+
+.image-actions .u-button {
+    flex: 1;
+    min-width: 60rpx;
+    font-size: 24rpx !important;
+    pointer-events: auto;
+    position: relative;
+    z-index: 11;
+}
+
+.image-name {
+    padding: 8rpx 12rpx;
+    background: #fff;
+    font-size: 22rpx;
+    color: #666;
+    text-align: center;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+
+// 自定义文件列表样式
+.custom-file-list {
+    margin-top: 20rpx;
+}
+
+.file-title {
+    font-size: 28rpx;
+    font-weight: 600;
+    color: #333;
+    margin-bottom: 20rpx;
+    padding-left: 10rpx;
+    border-left: 4rpx solid #2979ff;
+}
+
+.file-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(280rpx, 1fr));
+    gap: 20rpx;
+}
+
+.file-card {
+    position: relative;
+    background: #fff;
+    border-radius: 12rpx;
+    overflow: hidden;
+    border: 2rpx solid #f0f0f0;
+    transition: all 0.3s ease;
+}
+
+.file-card:active {
+    transform: scale(0.98);
+    border-color: #2979ff;
+}
+
+.file-icon-container {
+    position: relative;
+    width: 100%;
+    height: 200rpx;
+    border-radius: 8rpx;
+    overflow: hidden;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: #f8f9fa;
+}
+
+.file-name-display {
+    padding: 8rpx 12rpx;
+    background: #fff;
+    font-size: 22rpx;
+    color: #666;
+    text-align: center;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+
+// 涉及其他学生样式
+.involved-students {
+    margin-top: 20rpx;
+}
+
+.student-card {
+    background-color: #f9f9f9;
+    border-radius: 12rpx;
+    padding: 16rpx;
+    margin-bottom: 16rpx;
+    border: 1rpx solid #eee;
+}
+
+.student-header {
+    display: flex;
+    align-items: center;
+    margin-bottom: 12rpx;
+}
+
+.student-avatar {
+    width: 60rpx;
+    height: 60rpx;
+    border-radius: 50%;
+    background-color: #e0e0e0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 16rpx;
+}
+
+.student-basic {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+}
+
+.student-name {
+    font-size: 32rpx;
+    font-weight: bold;
+    color: #333;
+    margin-bottom: 4rpx;
+}
+
+.student-no {
+    font-size: 24rpx;
+    color: #666;
+}
+
+.student-gender {
+    margin-left: 16rpx;
+}
+
+.student-details {
+    display: flex;
+    flex-direction: column;
+    gap: 8rpx;
+    margin-top: 12rpx;
+    padding-left: 0rpx !important; /* Align with avatar */
+}
+
+.detail-row {
+    display: flex;
+    align-items: center;
+    gap: 10rpx;
+}
+
+.detail-label {
+    font-size: 28rpx;
+    color: #909399;
+    min-width: 100rpx;
+    flex-shrink: 0;
+}
+
+.detail-value {
+    font-size: 28rpx;
+    color: #303133;
+    flex: 1;
+}
+
+// 涉及其他学生(加载状态)样式
+.loading-involved-students {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 100rpx 20rpx;
+}
+
+.loading-text {
+    margin-top: 20rpx;
+    font-size: 28rpx;
+    color: #909399;
+}
+
+// 涉及其他学生(空状态)样式
+.empty-involved-students {
+    padding: 100rpx 20rpx;
+}
+
+// 涉及其他学生(错误状态)样式
+.error-involved-students {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 100rpx 20rpx;
+}
+
+.error-actions {
+    margin-top: 20rpx;
+}
+
+// 响应式适配
+@media screen and (max-width: 750rpx) {
+    .file-grid {
+        grid-template-columns: repeat(auto-fill, minmax(240rpx, 1fr));
+        gap: 15rpx;
+    }
+
+    .file-icon-container {
+        height: 160rpx;
+    }
+
+    .image-grid {
+        grid-template-columns: repeat(auto-fill, minmax(240rpx, 1fr));
+        gap: 15rpx;
+    }
+
+    .image-container {
+        height: 160rpx;
+    }
+
+    .file-actions .u-button,
+    .image-actions .u-button {
+        font-size: 22rpx !important;
+        padding: 6rpx 12rpx;
+    }
+
+    // 涉及学生卡片响应式调整
+    .student-card {
+        padding: 12rpx;
+        margin-bottom: 12rpx;
+    }
+
+    .student-avatar {
+        width: 50rpx;
+        height: 50rpx;
+        margin-right: 12rpx;
+    }
+
+    .student-name {
+        font-size: 28rpx;
+    }
+
+    .student-no {
+        font-size: 22rpx;
+    }
+
+    .student-details {
+        padding-left: 50rpx;
+        gap: 6rpx;
+    }
+
+    .detail-label {
+        font-size: 24rpx;
+        min-width: 80rpx;
+    }
+
+    .detail-value {
+        font-size: 24rpx;
+    }
+
+    .student-count {
+        font-size: 24rpx;
+    }
+
+    // 加载状态响应式调整
+    .loading-involved-students {
+        padding: 60rpx 20rpx;
+    }
+
+    .loading-text {
+        font-size: 24rpx;
+    }
+
+    // 错误状态响应式调整
+    .error-involved-students {
+        padding: 60rpx 20rpx;
+    }
+}
+
+// uview组件样式调整
+::v-deep .u-tag {
+    margin: 0;
+}
+</style>