zlwu 1 рік тому
батько
коміт
9825d83e52

+ 8 - 0
mobile/src/pages.json

@@ -183,6 +183,14 @@
 				"navigationBarBackgroundColor": "#0051d9",
 				"navigationBarTextStyle": "white"
 			}
+		},
+		{
+			"path": "pages/xgss/wsjc/wsjcAdd",
+			"style": {
+				"navigationBarTitleText": "新增宿舍卫生检查",
+				"navigationBarBackgroundColor": "#0051d9",
+				"navigationBarTextStyle": "white"
+			}
 		}
 	],
 	"globalStyle": {

+ 708 - 204
mobile/src/pages/xgss/wsjc/wsjc.vue

@@ -1,327 +1,831 @@
 <template>
     <view class="page-container">
-        <!-- 页面头部 -->
-        <view class="page-header">
-            <text class="page-title">宿舍卫生检查</text>
+        <!-- 顶部导航 -->
+        <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>
+
+        <!-- 搜索区域 -->
+        <view class="search-container">
+            <view class="search-row">
+                <view class="search-item">
+                    <text class="search-label">检查标题:</text>
+                    <u-input
+                        v-model="queryParams.checkTitle"
+                        placeholder="请输入检查标题"
+                        clearable
+                        border="none"
+                        style="width: 100%"
+                    />
+                </view>
+            </view>
+            <view class="search-actions">
+                <u-button type="primary" size="small" class="search-btn" @click="handleQuery">
+                    搜索
+                </u-button>
+                <u-button type="info" size="small" @click="resetQuery" style="margin-left: 10px">
+                    重置
+                </u-button>
+            </view>
         </view>
 
-        <!-- 主要内容区域 -->
-        <view class="main-content">
-            <!-- 功能卡片区域 -->
-            <view class="function-cards">
-                <!-- 检查记录 -->
-                <view class="function-card" @click="handleCheckRecord">
-                    <view class="card-icon">
-                        <u-icon name="list" size="32" color="#1890ff" />
-                    </view>
-                    <view class="card-content">
-                        <text class="card-title">检查记录</text>
-                        <text class="card-desc">查看历史检查记录</text>
-                    </view>
-                    <view class="card-arrow">
-                        <u-icon name="arrow-right" size="20" color="#999" />
-                    </view>
-                </view>
+        <!-- 加载状态 -->
+        <u-loading-page :loading="loading" loading-text="加载中..."></u-loading-page>
 
-                <!-- 新增检查 -->
-                <view class="function-card" @click="handleAddCheck">
-                    <view class="card-icon">
-                        <u-icon name="plus-circle" size="32" color="#52c41a" />
-                    </view>
-                    <view class="card-content">
-                        <text class="card-title">新增检查</text>
-                        <text class="card-desc">创建新的卫生检查</text>
-                    </view>
-                    <view class="card-arrow">
-                        <u-icon name="arrow-right" size="20" color="#999" />
-                    </view>
-                </view>
+        <!-- 卫生检查记录列表 -->
+        <view v-if="!loading" class="content-wrapper">
+            <!-- 空状态 -->
+            <view v-if="checkList.length === 0" class="empty-container">
+                <u-empty text="暂无检查记录" mode="data"></u-empty>
+            </view>
 
-                <!-- 统计报表 -->
-                <view class="function-card" @click="handleStatistics">
-                    <view class="card-icon">
-                        <u-icon name="chart" size="32" color="#fa8c16" />
+            <!-- 检查记录卡片列表 -->
+            <view v-else class="check-list">
+                <view v-for="(item, index) in checkList" :key="item.id || index" class="check-card">
+                    <!-- 卡片头部 -->
+                    <view class="card-header">
+                        <!-- 第一行:标题和卫生等级 -->
+                        <view class="header-row">
+                            <view class="check-title">{{ item.checkTitle || '-' }}</view>
+                            <view class="grade-section">
+                                <u-tag
+                                    :text="$getDictLabel(xg_dorm_hygiene_grade, item.hygieneGrade)"
+                                    :type="getHygieneGradeType(item.hygieneGrade)"
+                                    size="mini"
+                                ></u-tag>
+                            </view>
+                        </view>
+
+                        <!-- 第二行:检查类型、检查级别和总分 -->
+                        <view class="header-row second-row">
+                            <view class="left-section">
+                                <view class="check-type">
+                                    <!-- {{
+                                        $getDictLabel(xg_dorm_hygiene_check_type, item.checkType) ||
+                                        '-'
+                                    }} -->
+                                    <u-tag
+                                        :text="
+                                            $getDictLabel(
+                                                xg_dorm_hygiene_check_type,
+                                                item.checkType,
+                                            )
+                                        "
+                                        :type="getCheckLevelType(item.checkType)"
+                                        size="mini"
+                                    ></u-tag>
+                                </view>
+                                <view class="check-level">
+                                    <u-tag
+                                        :text="
+                                            $getDictLabel(
+                                                xg_dorm_hygiene_check_level,
+                                                item.checkLevel,
+                                            )
+                                        "
+                                        :type="getCheckLevelType(item.checkLevel)"
+                                        size="mini"
+                                    ></u-tag>
+                                </view>
+                            </view>
+                            <view class="score-section">
+                                <text class="total-score">{{ item.totalScore || '-' }}分</text>
+                            </view>
+                        </view>
                     </view>
+
+                    <!-- 卡片内容 -->
                     <view class="card-content">
-                        <text class="card-title">统计报表</text>
-                        <text class="card-desc">查看检查统计数据</text>
-                    </view>
-                    <view class="card-arrow">
-                        <u-icon name="arrow-right" size="20" color="#999" />
+                        <!-- 宿舍信息 -->
+                        <view class="info-section">
+                            <text class="section-title">宿舍信息</text>
+                            <view class="dorm-info-grid">
+                                <!-- 第一行:校区和宿舍区 -->
+                                <view class="dorm-row">
+                                    <view class="info-item-half">
+                                        <text class="label">校区:</text>
+                                        <text class="value">{{ item.campusName || '-' }}</text>
+                                    </view>
+                                    <view class="info-item-half">
+                                        <text class="label">宿舍区:</text>
+                                        <text class="value">{{ item.areaName || '-' }}</text>
+                                    </view>
+                                </view>
+                                <!-- 第二行:宿舍楼和房间 -->
+                                <view class="dorm-row">
+                                    <view class="info-item-half">
+                                        <text class="label">宿舍楼:</text>
+                                        <text class="value">{{ item.buildingName || '-' }}</text>
+                                    </view>
+                                    <view class="info-item-half">
+                                        <text class="label">房间:</text>
+                                        <text class="value">{{ item.roomName || '-' }}</text>
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+
+                        <!-- 检查信息 -->
+                        <view class="info-section">
+                            <text class="section-title">检查信息</text>
+                            <!-- 检查员和检查时间一行 -->
+                            <view class="check-details-row">
+                                <view class="info-item-half">
+                                    <text class="label">检查员:</text>
+                                    <text class="value">{{ item.checkerName || '-' }}</text>
+                                </view>
+                                <view class="check-time">
+                                    {{ formatTime(item.checkDate) }}
+                                </view>
+                            </view>
+                            <!-- 检查总结单独一行 -->
+                            <view class="info-item" v-if="item.checkSummary">
+                                <text class="label">检查总结:</text>
+                                <text class="value summary">{{ item.checkSummary }}</text>
+                            </view>
+                        </view>
                     </view>
-                </view>
-            </view>
 
-            <!-- 最近检查记录 -->
-            <view class="recent-section">
-                <view class="section-header">
-                    <text class="section-title">最近检查</text>
-                    <text class="section-more" @click="handleCheckRecord">查看更多</text>
-                </view>
-                
-                <view class="recent-list">
-                    <view class="recent-item" v-for="(item, index) in recentChecks" :key="index">
-                        <view class="item-left">
-                            <text class="item-title">{{ item.dormitory }}</text>
-                            <text class="item-time">{{ item.checkTime }}</text>
+                    <!-- 操作按钮 -->
+                    <view class="card-actions">
+                        <!-- 修改按钮 -->
+                        <view class="action-btn info-btn" @click="handleUpdate(item)">
+                            <u-icon name="edit-pen" size="16" color="#3c9cff"></u-icon>
+                            <text class="btn-text">修改</text>
                         </view>
-                        <view class="item-right">
-                            <view class="score-badge" :class="getScoreClass(item.score)">
-                                <text class="score-text">{{ item.score }}分</text>
-                            </view>
+
+                        <!-- 删除按钮 -->
+                        <view class="action-btn danger-btn" @click="handleDelete(item)">
+                            <u-icon name="trash" size="16" color="#f56c6c"></u-icon>
+                            <text class="btn-text">删除</text>
                         </view>
                     </view>
                 </view>
             </view>
         </view>
+
+        <!-- 分页加载 -->
+        <view v-if="checkList.length > 0" class="pagination-container">
+            <u-loadmore
+                :status="loadStatus"
+                @loadmore="loadMore"
+                load-text="上拉加载更多"
+                loading-text="正在加载..."
+                no-more-text="没有更多数据了"
+            />
+        </view>
+
+        <!-- 悬浮添加按钮 -->
+        <view class="fab-button" @click="handleAdd">
+            <u-icon name="plus" size="24" color="#ffffff" />
+        </view>
     </view>
 </template>
 
 <script>
+import { request } from '@/common/request.js';
+import { getDict, getDictLabel, getDictTagType } from '@/common/dict.js';
+
 export default {
     name: 'DormHygieneCheck',
     data() {
         return {
-            recentChecks: [
-                {
-                    dormitory: '1号楼101室',
-                    checkTime: '2024-01-15 14:30',
-                    score: 95
-                },
-                {
-                    dormitory: '2号楼203室',
-                    checkTime: '2024-01-14 15:20',
-                    score: 88
-                },
-                {
-                    dormitory: '3号楼305室',
-                    checkTime: '2024-01-13 16:10',
-                    score: 92
-                }
-            ]
+            loading: false,
+
+            // 查询参数
+            queryParams: {
+                pageNum: 1,
+                pageSize: 10,
+                checkTitle: '',
+            },
+
+            // 检查记录列表
+            checkList: [],
+            total: 0,
+
+            // 分页加载状态
+            loadStatus: 'loadmore', // loadmore, loading, nomore
+
+            // 字典数据
+            xg_dorm_hygiene_check_type: [],
+            xg_dorm_hygiene_check_level: [],
+            xg_dorm_hygiene_grade: [],
         };
     },
+
+    async created() {
+        // 绑定 getDictLabel 函数到组件实例
+        this.$getDictLabel = getDictLabel;
+
+        // 加载字典数据
+        await this.loadDictionaries();
+        // 加载列表数据
+        await this.loadCheckList();
+    },
+
+    mounted() {
+        // 监听刷新事件
+        uni.$on('refreshCheckList', this.handleRefreshEvent);
+    },
+
+    beforeDestroy() {
+        // 移除事件监听
+        uni.$off('refreshCheckList', this.handleRefreshEvent);
+    },
+
+    // 页面滚动到底部事件
+    onReachBottom() {
+        console.log('🔄 触发滚动到底部事件,开始加载更多...');
+        this.loadMore();
+    },
+
     methods: {
-        // 处理检查记录
-        handleCheckRecord() {
-            uni.showToast({
-                title: '功能开发中',
-                icon: 'none',
-                duration: 2000
-            });
+        // 处理刷新事件
+        handleRefreshEvent() {
+            console.log('🔄 收到刷新事件,正在刷新检查列表...');
+            this.loadCheckList();
+        },
+
+        // 返回上一页
+        goBack() {
+            uni.navigateBack();
+        },
+
+        // 加载字典数据
+        async loadDictionaries() {
+            try {
+                console.log('🔍 开始加载字典数据...');
+
+                const dictTypes = [
+                    'xg_dorm_hygiene_check_type',
+                    'xg_dorm_hygiene_check_level',
+                    'xg_dorm_hygiene_grade',
+                ];
+
+                // 使用 getDict 函数获取已转换格式的字典数据
+                const dictPromises = dictTypes.map((dictType) => getDict(dictType));
+
+                const results = await Promise.all(dictPromises);
+                this.xg_dorm_hygiene_check_type = results[0] || [];
+                this.xg_dorm_hygiene_check_level = results[1] || [];
+                this.xg_dorm_hygiene_grade = results[2] || [];
+
+                console.log('✅ 字典数据加载完成:', {
+                    xg_dorm_hygiene_check_type: this.xg_dorm_hygiene_check_type.length,
+                    xg_dorm_hygiene_check_level: this.xg_dorm_hygiene_check_level.length,
+                    xg_dorm_hygiene_grade: this.xg_dorm_hygiene_grade.length,
+                });
+            } catch (error) {
+                console.error('❌ 加载字典数据失败:', error);
+            }
+        },
+
+        // 加载检查记录列表
+        async loadCheckList(isRefresh = true) {
+            if (isRefresh) {
+                this.loading = true;
+                this.queryParams.pageNum = 1;
+                this.checkList = [];
+            } else {
+                this.loadStatus = 'loading';
+            }
+
+            try {
+                console.log('📋 开始加载检查记录列表...', this.queryParams);
+
+                const res = await request({
+                    url: '/dormHygieneCheck/dormHygieneCheck/list',
+                    method: 'GET',
+                    params: this.queryParams,
+                });
+
+                if (res && res.code === 200) {
+                    const newData = res.rows || [];
+
+                    if (isRefresh) {
+                        this.checkList = newData;
+                    } else {
+                        this.checkList = [...this.checkList, ...newData];
+                    }
+
+                    this.total = res.total || 0;
+
+                    // 更新分页状态
+                    if (newData.length < this.queryParams.pageSize) {
+                        this.loadStatus = 'nomore';
+                    } else {
+                        this.loadStatus = 'loadmore';
+                    }
+
+                    console.log('✅ 检查记录加载成功:', {
+                        当前页: this.queryParams.pageNum,
+                        本次加载: newData.length,
+                        总数: this.total,
+                        列表长度: this.checkList.length,
+                    });
+                } else {
+                    console.warn('获取检查记录失败:', res?.msg);
+                    uni.showToast({
+                        title: res?.msg || '获取数据失败',
+                        icon: 'none',
+                    });
+
+                    if (!isRefresh) {
+                        this.loadStatus = 'loadmore';
+                    }
+                }
+            } catch (error) {
+                console.error('❌ 加载检查记录失败:', error);
+                uni.showToast({
+                    title: '获取数据失败',
+                    icon: 'none',
+                });
+
+                if (!isRefresh) {
+                    this.loadStatus = 'loadmore';
+                }
+            } finally {
+                if (isRefresh) {
+                    this.loading = false;
+                }
+            }
+        },
+
+        // 加载更多
+        async loadMore() {
+            // 防止重复加载和无效加载
+            if (this.loadStatus !== 'loadmore' || this.loading) {
+                console.log('🚫 跳过加载更多:', {
+                    loadStatus: this.loadStatus,
+                    loading: this.loading,
+                });
+                return;
+            }
+
+            console.log('📄 开始加载更多数据...');
+            this.queryParams.pageNum += 1;
+            await this.loadCheckList(false);
+        },
+
+        // 搜索操作
+        handleQuery() {
+            console.log('🔍 执行搜索:', this.queryParams);
+            this.loadCheckList();
         },
 
-        // 处理新增检查
-        handleAddCheck() {
-            uni.showToast({
-                title: '功能开发中',
-                icon: 'none',
-                duration: 2000
+        // 重置搜索
+        resetQuery() {
+            this.queryParams.checkTitle = '';
+            this.loadCheckList();
+        },
+
+        // 获取检查级别类型
+        getCheckLevelType(level) {
+            const fallbackMap = {
+                1: 'info', // 宿管员检查
+                2: 'warning', // 学院检查
+                3: 'success', // 学校检查
+            };
+            return getDictTagType(this.xg_dorm_hygiene_check_level, level, fallbackMap);
+        },
+
+        // 获取检查类型类型
+        getCheckTypeType(type) {
+            const fallbackMap = {
+                1: 'info', // 日常检查
+                2: 'warning', // 专项检查
+                3: 'error', // 突击检查
+            };
+            return getDictTagType(this.xg_dorm_hygiene_check_type, type, fallbackMap);
+        },
+
+        // 获取卫生等级类型
+        getHygieneGradeType(grade) {
+            const fallbackMap = {
+                A: 'success', // 优秀
+                B: 'warning', // 良好
+                C: 'info', // 一般
+                D: 'error', // 较差
+            };
+            return getDictTagType(this.xg_dorm_hygiene_grade, grade, fallbackMap);
+        },
+
+        // 格式化时间
+        formatTime(time) {
+            if (!time) return '-';
+            try {
+                // 处理时间字符串,保持完整格式 YYYY-MM-DD HH:mm:ss
+                return time.replace('T', ' ').substring(0, 19);
+            } catch (error) {
+                return time;
+            }
+        },
+
+        // 新增检查
+        handleAdd() {
+            uni.navigateTo({
+                url: '/pages/xgss/wsjc/wsjcAdd?type=add',
             });
         },
 
-        // 处理统计报表
-        handleStatistics() {
-            uni.showToast({
-                title: '功能开发中',
-                icon: 'none',
-                duration: 2000
+        // 修改检查
+        handleUpdate(item) {
+            uni.navigateTo({
+                url: `/pages/xgss/wsjc/wsjcAdd?id=${item.id}&type=update`,
             });
         },
 
-        // 获取分数样式类
-        getScoreClass(score) {
-            if (score >= 90) return 'score-excellent';
-            if (score >= 80) return 'score-good';
-            if (score >= 70) return 'score-average';
-            return 'score-poor';
-        }
-    }
+        // 删除检查
+        async handleDelete(item) {
+            try {
+                const confirm = await new Promise((resolve) => {
+                    uni.showModal({
+                        title: '确认删除',
+                        content: '是否确认删除这条检查记录?',
+                        success: (res) => resolve(res.confirm),
+                        fail: () => resolve(false),
+                    });
+                });
+
+                if (!confirm) return;
+
+                uni.showLoading({ title: '删除中...' });
+
+                const res = await request({
+                    url: `/dormHygieneCheck/dormHygieneCheck/${item.id}`,
+                    method: 'DELETE',
+                });
+
+                uni.hideLoading();
+
+                if (res && res.code === 200) {
+                    uni.showToast({
+                        title: '删除成功',
+                        icon: 'success',
+                    });
+
+                    // 刷新列表
+                    this.loadCheckList();
+                } else {
+                    uni.showToast({
+                        title: res?.msg || '删除失败',
+                        icon: 'none',
+                    });
+                }
+            } catch (error) {
+                uni.hideLoading();
+                console.error('❌ 删除检查记录失败:', error);
+                uni.showToast({
+                    title: '删除失败',
+                    icon: 'none',
+                });
+            }
+        },
+    },
 };
 </script>
 
 <style lang="scss" scoped>
 .page-container {
     min-height: 100vh;
-    background-color: #f5f5f5;
-    padding: 16px;
+    background-color: #f5f6fa;
+    padding-bottom: 80px;
+}
+
+// 搜索区域
+.search-container {
+    background-color: #ffffff;
+    padding: 20rpx;
+    margin-bottom: 8px;
+    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+}
+
+.search-row {
+    display: flex;
+    flex-direction: row;
+    gap: 20rpx;
+}
+
+.search-item {
+    display: flex;
+    flex-direction: column;
+    flex: 1;
+
+    .search-label {
+        font-size: 28rpx;
+        color: #606266;
+        margin-bottom: 8rpx;
+    }
 }
 
-.page-header {
-    margin-bottom: 20px;
+.search-actions {
+    display: flex;
+    justify-content: center;
+    margin-top: 20rpx;
+    gap: 20rpx;
+}
+
+// 内容区域
+.content-wrapper {
+    padding: 20rpx;
 }
 
-.page-title {
-    font-size: 24px;
-    font-weight: 600;
-    color: #333;
+.empty-container {
+    padding: 100rpx 20rpx;
 }
 
-.main-content {
+// 检查列表
+.check-list {
     display: flex;
     flex-direction: column;
-    gap: 20px;
+    gap: 20rpx;
+}
+
+.check-card {
+    background-color: #ffffff;
+    border-radius: 16rpx;
+    box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
+    overflow: hidden;
 }
 
-.function-cards {
+// 卡片头部
+.card-header {
     display: flex;
     flex-direction: column;
-    gap: 12px;
+    padding: 24rpx;
+    border-bottom: 1px solid #f0f0f0;
+    background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
 }
 
-.function-card {
-    background: #ffffff;
-    border-radius: 12px;
-    padding: 16px;
+.header-row {
     display: flex;
+    justify-content: space-between;
     align-items: center;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-    transition: all 0.3s ease;
+    margin-bottom: 8rpx;
+}
 
-    &:active {
-        transform: scale(0.98);
-        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
-    }
+.second-row {
+    margin-bottom: 0;
 }
 
-.card-icon {
-    margin-right: 16px;
+.left-section {
+    display: flex;
+    align-items: center;
+    gap: 16rpx;
 }
 
-.card-content {
-    flex: 1;
+.score-section {
     display: flex;
-    flex-direction: column;
+    align-items: center;
+    gap: 8rpx;
+}
+
+.total-score {
+    font-size: 28rpx;
+    font-weight: bold;
+    color: #007aff;
 }
 
-.card-title {
-    font-size: 16px;
-    font-weight: 600;
-    color: #333;
-    margin-bottom: 4px;
+.check-title {
+    font-size: 32rpx;
+    font-weight: bold;
+    color: #303133;
+    flex: 1;
 }
 
-.card-desc {
-    font-size: 14px;
-    color: #666;
+.check-level {
+    flex-shrink: 0;
 }
 
-.card-arrow {
-    margin-left: 8px;
+.check-type {
+    font-size: 28rpx;
+    color: #606266;
+    font-weight: 500;
 }
 
-.recent-section {
-    background: #ffffff;
-    border-radius: 12px;
-    padding: 16px;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+.grade-section {
+    display: flex;
+    justify-content: flex-end;
 }
 
-.section-header {
+.check-details-row {
     display: flex;
     justify-content: space-between;
-    align-items: center;
-    margin-bottom: 16px;
+    gap: 20rpx;
+    margin-top: 8rpx;
 }
 
-.section-title {
-    font-size: 18px;
-    font-weight: 600;
-    color: #333;
+.check-time {
+    font-size: 24rpx;
+    color: #999999;
+    text-align: right;
 }
 
-.section-more {
-    font-size: 14px;
-    color: #1890ff;
-    cursor: pointer;
+// 卡片内容
+.card-content {
+    padding: 24rpx;
 }
 
-.recent-list {
-    display: flex;
-    flex-direction: column;
-    gap: 12px;
+.info-section {
+    margin-bottom: 24rpx;
+
+    &:last-child {
+        margin-bottom: 0;
+    }
+}
+
+.section-title {
+    font-size: 28rpx;
+    font-weight: bold;
+    color: #303133;
+    margin-bottom: 16rpx;
+    padding-bottom: 8rpx;
+    // border-bottom: 2rpx solid #e4e7ed;
+    display: block;
 }
 
-.recent-item {
+.info-item {
     display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 12px 0;
-    border-bottom: 1px solid #f0f0f0;
+    align-items: flex-start;
+    margin-bottom: 12rpx;
+    line-height: 1.5;
 
     &:last-child {
-        border-bottom: none;
+        margin-bottom: 0;
     }
 }
 
-.item-left {
+.dorm-info-grid {
     display: flex;
     flex-direction: column;
+    gap: 16rpx;
 }
 
-.item-title {
-    font-size: 16px;
-    font-weight: 500;
-    color: #333;
-    margin-bottom: 4px;
+.dorm-row {
+    display: flex;
+    justify-content: space-between;
+    gap: 20rpx;
 }
 
-.item-time {
-    font-size: 14px;
-    color: #999;
+.info-item-half {
+    flex: 1;
+    display: flex;
+    align-items: flex-start;
+
+    .label {
+        font-size: 26rpx;
+        color: #606266;
+        min-width: 100rpx;
+        margin-right: 8rpx;
+        flex-shrink: 0;
+    }
+
+    .value {
+        font-size: 26rpx;
+        color: #303133;
+        flex: 1;
+        word-break: break-all;
+    }
+}
+
+.label {
+    font-size: 26rpx;
+    color: #606266;
+    min-width: 120rpx;
+    margin-right: 16rpx;
+    flex-shrink: 0;
 }
 
-.item-right {
+.value {
+    font-size: 26rpx;
+    color: #303133;
+    flex: 1;
+    word-break: break-all;
+
+    &.summary {
+        line-height: 1.6;
+    }
+}
+
+// 操作按钮
+.card-actions {
     display: flex;
-    align-items: center;
+    justify-content: flex-end;
+    padding: 20rpx;
+    border-top: 1px solid #f0f0f0;
+    gap: 16rpx;
 }
 
-.score-badge {
-    padding: 4px 12px;
-    border-radius: 16px;
+.action-btn {
     display: flex;
+    flex-direction: column;
     align-items: center;
     justify-content: center;
+    padding: 16rpx 20rpx;
+    border-radius: 12rpx;
+    min-width: 120rpx;
+    transition: all 0.3s ease;
+    background-color: transparent;
+
+    &:active {
+        transform: scale(0.95);
+        opacity: 0.8;
+    }
+}
+
+.btn-text {
+    font-size: 24rpx;
+    margin-top: 8rpx;
+    font-weight: 500;
 }
 
-.score-excellent {
-    background-color: #f6ffed;
-    border: 1px solid #b7eb8f;
+.info-btn .btn-text {
+    color: #3c9cff;
 }
 
-.score-good {
-    background-color: #e6f7ff;
-    border: 1px solid #91d5ff;
+.danger-btn .btn-text {
+    color: #f56c6c;
 }
 
-.score-average {
-    background-color: #fff7e6;
-    border: 1px solid #ffd591;
+// 分页容器
+.pagination-container {
+    padding: 20rpx;
 }
 
-.score-poor {
-    background-color: #fff2f0;
-    border: 1px solid #ffccc7;
+// 搜索按钮样式
+::v-deep .search-btn {
+    background: linear-gradient(135deg, #007aff, #0051d9) !important;
+    border: none !important;
+    box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3) !important;
+    transition: all 0.3s ease !important;
+
+    &:active {
+        transform: scale(0.95) !important;
+        box-shadow: 0 2px 8px rgba(0, 122, 255, 0.4) !important;
+    }
 }
 
-.score-text {
-    font-size: 14px;
-    font-weight: 600;
+// 悬浮按钮
+.fab-button {
+    position: fixed;
+    right: 20px;
+    bottom: 30px;
+    width: 56px;
+    height: 56px;
+    border-radius: 50%;
+    background: linear-gradient(135deg, #007aff, #0051d9);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3);
+    transition: all 0.3s ease;
+    z-index: 999;
+
+    &:active {
+        transform: scale(0.95);
+        box-shadow: 0 2px 8px rgba(0, 122, 255, 0.4);
+    }
 }
 
-.score-excellent .score-text {
-    color: #52c41a;
+// 响应式设计
+@media (max-width: 375px) {
+    .search-container {
+        padding: 16rpx;
+    }
+
+    .content-wrapper {
+        padding: 16rpx;
+    }
+
+    .check-card {
+        margin-bottom: 16rpx;
+    }
+
+    .card-header,
+    .card-content {
+        padding: 20rpx;
+    }
+
+    .fab-button {
+        right: 16px;
+        bottom: 24px;
+        width: 48px;
+        height: 48px;
+    }
 }
 
-.score-good .score-text {
-    color: #1890ff;
+// uview组件样式调整
+::v-deep .u-tag {
+    margin: 0;
 }
 
-.score-average .score-text {
-    color: #fa8c16;
+::v-deep .u-input {
+    font-size: 26rpx;
 }
 
-.score-poor .score-text {
-    color: #f5222d;
+::v-deep .u-button {
+    border-radius: 8rpx;
 }
 </style>

+ 1558 - 0
mobile/src/pages/xgss/wsjc/wsjcAdd.vue

@@ -0,0 +1,1558 @@
+<template>
+    <view class="page-container">
+        <!-- 顶部导航 -->
+        <u-navbar :title="navTitle" :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="pageLoading" loading-text="加载中..."></u-loading-page>
+
+        <!-- 表单内容 -->
+        <view v-if="!pageLoading" class="form-container">
+            <u-form ref="formRef" :model="formData" :rules="rules" label-width="140">
+                <!-- 基本信息 -->
+                <view class="form-section">
+                    <text class="section-title">基本信息</text>
+
+                    <u-form-item label="检查标题" prop="checkTitle" required>
+                        <u-input v-model="formData.checkTitle" placeholder="请输入检查标题" />
+                    </u-form-item>
+
+                    <u-form-item label="检查日期" prop="checkDate" required>
+                        <u-datetime-picker
+                            :show="showDatePicker"
+                            v-model="checkDateValue"
+                            mode="datetime"
+                            @confirm="handleDateConfirm"
+                            @cancel="showDatePicker = false"
+                        ></u-datetime-picker>
+                        <view @click="showDatePicker = true">
+                            <u-input
+                                v-model="checkDateDisplay"
+                                placeholder="请选择检查日期"
+                                readonly
+                                suffix-icon="calendar-fill"
+                                suffix-icon-style="color: #909399"
+                            />
+                        </view>
+                    </u-form-item>
+
+                    <u-form-item label="检查类型" prop="checkType" required>
+                        <u-picker
+                            :show="showCheckTypePicker"
+                            :columns="checkTypeColumns"
+                            @confirm="handleCheckTypeConfirm"
+                            @cancel="showCheckTypePicker = false"
+                        ></u-picker>
+                        <view @click="showCheckTypePicker = true">
+                            <u-input
+                                v-model="checkTypeDisplay"
+                                placeholder="请选择检查类型"
+                                readonly
+                                suffix-icon="arrow-down-fill"
+                            />
+                        </view>
+                    </u-form-item>
+
+                    <u-form-item label="检查级别" prop="checkLevel" required>
+                        <u-picker
+                            :show="showCheckLevelPicker"
+                            :columns="checkLevelColumns"
+                            @confirm="handleCheckLevelConfirm"
+                            @cancel="showCheckLevelPicker = false"
+                        ></u-picker>
+                        <view @click="showCheckLevelPicker = true">
+                            <u-input
+                                v-model="checkLevelDisplay"
+                                placeholder="请选择检查级别"
+                                readonly
+                                suffix-icon="arrow-down-fill"
+                            />
+                        </view>
+                    </u-form-item>
+                </view>
+
+                <!-- 位置信息 -->
+                <view class="form-section">
+                    <text class="section-title">位置信息</text>
+
+                    <u-form-item label="校区" prop="campusId" required>
+                        <u-picker
+                            :show="showCampusPicker"
+                            :columns="campusColumns"
+                            @confirm="handleCampusConfirm"
+                            @cancel="showCampusPicker = false"
+                        ></u-picker>
+                        <view @click="showCampusPicker = true">
+                            <u-input
+                                v-model="formData.campusName"
+                                placeholder="请选择校区"
+                                readonly
+                                suffix-icon="arrow-down-fill"
+                            />
+                        </view>
+                    </u-form-item>
+
+                    <u-form-item label="宿舍区" prop="areaId" required>
+                        <u-picker
+                            :show="showAreaPicker"
+                            :columns="areaColumns"
+                            @confirm="handleAreaConfirm"
+                            @cancel="showAreaPicker = false"
+                        ></u-picker>
+                        <view @click="showAreaPicker = true">
+                            <u-input
+                                v-model="formData.areaName"
+                                placeholder="请选择宿舍区"
+                                readonly
+                                suffix-icon="arrow-down-fill"
+                                :disabled="!formData.campusId"
+                            />
+                        </view>
+                    </u-form-item>
+
+                    <u-form-item label="宿舍楼" prop="buildingId" required>
+                        <u-picker
+                            :show="showBuildingPicker"
+                            :columns="buildingColumns"
+                            @confirm="handleBuildingConfirm"
+                            @cancel="showBuildingPicker = false"
+                        ></u-picker>
+                        <view @click="showBuildingPicker = true">
+                            <u-input
+                                v-model="formData.buildingName"
+                                placeholder="请选择宿舍楼"
+                                readonly
+                                suffix-icon="arrow-down-fill"
+                                :disabled="!formData.areaId"
+                            />
+                        </view>
+                    </u-form-item>
+
+                    <u-form-item label="房间" prop="roomId" required>
+                        <u-picker
+                            :show="showRoomPicker"
+                            :columns="roomColumns"
+                            @confirm="handleRoomConfirm"
+                            @cancel="showRoomPicker = false"
+                        ></u-picker>
+                        <view @click="showRoomPicker = true">
+                            <u-input
+                                v-model="formData.roomName"
+                                placeholder="请选择房间"
+                                readonly
+                                suffix-icon="arrow-down-fill"
+                                :disabled="!formData.buildingId"
+                            />
+                        </view>
+                    </u-form-item>
+                </view>
+
+                <!-- 检查结果 -->
+                <view class="form-section">
+                    <text class="section-title">检查结果</text>
+
+                    <u-form-item label="宿舍总分" prop="totalScore">
+                        <u-input
+                            v-model="formData.totalScore"
+                            type="number"
+                            placeholder="请输入宿舍总分(0-100分)"
+                            @input="handleTotalScoreChange"
+                        />
+                    </u-form-item>
+
+                    <u-form-item label="卫生等级" prop="hygieneGrade" required>
+                        <u-picker
+                            :show="showHygieneGradePicker"
+                            :columns="hygieneGradeColumns"
+                            @confirm="handleHygieneGradeConfirm"
+                            @cancel="showHygieneGradePicker = false"
+                        ></u-picker>
+                        <view @click="showHygieneGradePicker = true">
+                            <u-input
+                                v-model="hygieneGradeDisplay"
+                                placeholder="请选择卫生等级"
+                                readonly
+                                suffix-icon="arrow-down-fill"
+                            />
+                        </view>
+                    </u-form-item>
+                </view>
+
+                <!-- 检查员信息 -->
+                <view class="form-section">
+                    <text class="section-title">检查员信息</text>
+
+                    <u-form-item label="检查员" prop="checkerName" required>
+                        <u-input v-model="formData.checkerName" placeholder="请输入检查员姓名" />
+                    </u-form-item>
+                </view>
+
+                <!-- 房间学生信息 -->
+                <view v-if="formData.roomId && roomStudents.length > 0" class="form-section">
+                    <text class="section-title">房间学生信息</text>
+
+                    <view class="student-list">
+                        <view
+                            v-for="(student, index) in roomStudents"
+                            :key="index"
+                            class="student-item"
+                        >
+                            <view class="student-header">
+                                <text class="student-name">{{ student.studentName }}</text>
+                                <text class="student-no">{{ student.studentNo }}</text>
+                                <text class="student-bed">{{ student.bedName }}</text>
+                            </view>
+                            <view class="student-form">
+                                <view class="form-row">
+                                    <view class="form-col">
+                                        <text class="form-label">个人得分</text>
+                                        <u-input
+                                            v-model="student.student_score"
+                                            type="number"
+                                            placeholder="0-100分"
+                                            @input="handleStudentScoreChange(student)"
+                                        />
+                                    </view>
+                                    <view class="form-col">
+                                        <text class="form-label">出勤状态</text>
+                                        <u-picker
+                                            :show="student.showAttendancePicker"
+                                            :columns="attendanceStatusColumns"
+                                            @confirm="(e) => handleAttendanceConfirm(e, student)"
+                                            @cancel="student.showAttendancePicker = false"
+                                        ></u-picker>
+                                        <view @click="student.showAttendancePicker = true">
+                                            <u-input
+                                                :value="
+                                                    getAttendanceDisplay(student.attendance_status)
+                                                "
+                                                placeholder="请选择"
+                                                readonly
+                                                suffix-icon="arrow-down-fill"
+                                                size="small"
+                                            />
+                                        </view>
+                                    </view>
+                                </view>
+                                <view class="form-row">
+                                    <view class="form-col">
+                                        <text class="form-label">学生评级</text>
+                                        <u-picker
+                                            :show="student.showGradePicker"
+                                            :columns="hygieneGradeColumns"
+                                            @confirm="(e) => handleStudentGradeConfirm(e, student)"
+                                            @cancel="student.showGradePicker = false"
+                                        ></u-picker>
+                                        <view @click="student.showGradePicker = true">
+                                            <u-input
+                                                :value="getGradeDisplay(student.student_grade)"
+                                                placeholder="请选择"
+                                                readonly
+                                                suffix-icon="arrow-down-fill"
+                                                size="small"
+                                            />
+                                        </view>
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 附件上传 -->
+                <view class="form-section">
+                    <text class="section-title">附件上传</text>
+
+                    <!-- 图片附件 -->
+                    <view class="attachment-section">
+                        <text class="attachment-title">图片附件</text>
+                        <view class="image-upload-container">
+                            <view class="image-list">
+                                <view
+                                    v-for="(image, index) in imageList"
+                                    :key="index"
+                                    class="image-item"
+                                >
+                                    <image
+                                        :src="image.url"
+                                        mode="aspectFill"
+                                        class="image-preview"
+                                        @click="previewImage(index)"
+                                    />
+                                    <view class="image-actions">
+                                        <u-icon
+                                            name="trash"
+                                            size="16"
+                                            color="#f56c6c"
+                                            @click="removeImage(index)"
+                                        />
+                                    </view>
+                                </view>
+                                <view
+                                    v-if="imageList.length < 9"
+                                    class="image-upload-btn"
+                                    @click="chooseImage"
+                                >
+                                    <u-icon name="plus" size="32" color="#909399" />
+                                    <text class="upload-text">上传图片</text>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+
+                    <!-- 文档附件 -->
+                    <view class="attachment-section">
+                        <text class="attachment-title">文档附件</text>
+                        <view class="document-upload-container">
+                            <view class="document-list">
+                                <view
+                                    v-for="(doc, index) in documentList"
+                                    :key="index"
+                                    class="document-item"
+                                >
+                                    <view class="document-info">
+                                        <u-icon name="file-text" size="24" color="#3c9cff" />
+                                        <text class="document-name">{{ doc.name }}</text>
+                                    </view>
+                                    <view class="document-actions">
+                                        <u-icon
+                                            name="download"
+                                            size="16"
+                                            color="#52c41a"
+                                            @click="downloadDocument(doc)"
+                                        />
+                                        <u-icon
+                                            name="trash"
+                                            size="16"
+                                            color="#f56c6c"
+                                            @click="removeDocument(index)"
+                                        />
+                                    </view>
+                                </view>
+                            </view>
+                            <view class="document-upload-btn" @click="chooseDocument">
+                                <u-icon name="folder-plus" size="20" color="#3c9cff" />
+                                <text class="upload-text">选择文档</text>
+                            </view>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 备注信息 -->
+                <view class="form-section">
+                    <text class="section-title">备注信息</text>
+
+                    <u-form-item label="检查总结" prop="checkSummary">
+                        <u-textarea
+                            v-model="formData.checkSummary"
+                            placeholder="请输入检查总结"
+                            :maxlength="500"
+                            count
+                            :height="120"
+                        />
+                    </u-form-item>
+
+                    <u-form-item label="整改建议" prop="improvementSuggestions">
+                        <u-textarea
+                            v-model="formData.improvementSuggestions"
+                            placeholder="请输入整改建议"
+                            :maxlength="500"
+                            count
+                            :height="120"
+                        />
+                    </u-form-item>
+
+                    <u-form-item label="检查备注" prop="checkRemarks">
+                        <u-textarea
+                            v-model="formData.checkRemarks"
+                            placeholder="请输入检查备注"
+                            :maxlength="500"
+                            count
+                            :height="120"
+                        />
+                    </u-form-item>
+                </view>
+            </u-form>
+        </view>
+
+        <!-- 底部操作按钮 -->
+        <view class="bottom-actions">
+            <u-button type="info" @click="goBack" style="margin-right: 20rpx">取消</u-button>
+            <u-button type="primary" @click="handleSubmit" :loading="submitLoading">保存</u-button>
+        </view>
+    </view>
+</template>
+
+<script>
+import { request, smartUploadFile } from '@/common/request.js';
+import { getDict, getDictLabel } from '@/common/dict.js';
+
+export default {
+    name: 'DormHygieneCheckAdd',
+    data() {
+        return {
+            pageLoading: false,
+            submitLoading: false,
+
+            // 页面参数
+            pageType: 'add', // add | update
+            checkId: null,
+
+            // 表单数据
+            formData: {
+                id: undefined,
+                checkTitle: '',
+                checkDate: '',
+                checkerId: undefined,
+                checkerName: '',
+                checkType: '1',
+                checkLevel: '1',
+                roomId: undefined,
+                roomCode: undefined,
+                roomName: undefined,
+                buildingId: undefined,
+                buildingName: undefined,
+                areaId: undefined,
+                areaName: undefined,
+                campusId: undefined,
+                campusName: undefined,
+                totalScore: undefined,
+                hygieneGrade: 'A',
+                attachmentIds: undefined,
+                checkSummary: undefined,
+                improvementSuggestions: undefined,
+                checkRemarks: undefined,
+            },
+
+            // 表单验证规则
+            rules: {
+                checkTitle: [{ required: true, message: '请输入检查标题', trigger: 'blur' }],
+                checkDate: [{ required: true, message: '请选择检查日期', trigger: 'change' }],
+                checkerName: [{ required: true, message: '请输入检查员姓名', trigger: 'blur' }],
+                campusId: [{ required: true, message: '请选择校区', trigger: 'change' }],
+                areaId: [{ required: true, message: '请选择宿舍区', trigger: 'change' }],
+                buildingId: [{ required: true, message: '请选择宿舍楼', trigger: 'change' }],
+                roomId: [{ required: true, message: '请选择房间', trigger: 'change' }],
+                checkType: [{ required: true, message: '请选择检查类型', trigger: 'change' }],
+                checkLevel: [{ required: true, message: '请选择检查级别', trigger: 'change' }],
+                hygieneGrade: [{ required: true, message: '请选择卫生等级', trigger: 'change' }],
+            },
+
+            // 时间选择器
+            showDatePicker: false,
+            checkDateValue: '',
+            checkDateDisplay: '',
+
+            // 级联选择器数据
+            campusOptions: [],
+            areaOptions: [],
+            buildingOptions: [],
+            roomOptions: [],
+
+            // 级联选择器显示状态
+            showCampusPicker: false,
+            showAreaPicker: false,
+            showBuildingPicker: false,
+            showRoomPicker: false,
+
+            // 字典选择器显示状态
+            showCheckTypePicker: false,
+            showCheckLevelPicker: false,
+            showHygieneGradePicker: false,
+
+            // 字典数据
+            xg_dorm_hygiene_check_type: [],
+            xg_dorm_hygiene_check_level: [],
+            xg_dorm_hygiene_grade: [],
+            xg_student_attendance_status: [],
+
+            // 房间学生信息
+            roomStudents: [],
+
+            // 附件数据
+            imageList: [],
+            documentList: [],
+        };
+    },
+
+    computed: {
+        navTitle() {
+            return this.pageType === 'add' ? '新增检查' : '修改检查';
+        },
+
+        // 级联选择器数据转换
+        campusColumns() {
+            return [this.campusOptions.map((item) => ({ text: item.campusName, value: item.id }))];
+        },
+
+        areaColumns() {
+            return [this.areaOptions.map((item) => ({ text: item.areaName, value: item.id }))];
+        },
+
+        buildingColumns() {
+            return [
+                this.buildingOptions.map((item) => ({ text: item.buildingName, value: item.id })),
+            ];
+        },
+
+        roomColumns() {
+            return [this.roomOptions.map((item) => ({ text: item.roomName, value: item.id }))];
+        },
+
+        // 字典选择器数据转换
+        checkTypeColumns() {
+            return [
+                this.xg_dorm_hygiene_check_type.map((item) => ({
+                    text: item.label,
+                    value: item.value,
+                })),
+            ];
+        },
+
+        checkLevelColumns() {
+            return [
+                this.xg_dorm_hygiene_check_level.map((item) => ({
+                    text: item.label,
+                    value: item.value,
+                })),
+            ];
+        },
+
+        hygieneGradeColumns() {
+            return [
+                this.xg_dorm_hygiene_grade.map((item) => ({ text: item.label, value: item.value })),
+            ];
+        },
+
+        attendanceStatusColumns() {
+            return [
+                this.xg_student_attendance_status.map((item) => ({
+                    text: item.label,
+                    value: item.value,
+                })),
+            ];
+        },
+
+        // 显示字段
+        checkTypeDisplay() {
+            const item = this.xg_dorm_hygiene_check_type.find(
+                (d) => d.value === this.formData.checkType,
+            );
+            return item ? item.label : '';
+        },
+
+        checkLevelDisplay() {
+            const item = this.xg_dorm_hygiene_check_level.find(
+                (d) => d.value === this.formData.checkLevel,
+            );
+            return item ? item.label : '';
+        },
+
+        hygieneGradeDisplay() {
+            const item = this.xg_dorm_hygiene_grade.find(
+                (d) => d.value === this.formData.hygieneGrade,
+            );
+            return item ? item.label : '';
+        },
+    },
+
+    async onLoad(options) {
+        console.log('页面参数:', options);
+
+        this.pageType = options.type || 'add';
+        this.checkId = options.id;
+
+        await this.initPage();
+    },
+
+    methods: {
+        // 初始化页面
+        async initPage() {
+            this.pageLoading = true;
+
+            try {
+                // 加载字典数据
+                await this.loadDictionaries();
+                // 加载级联选择器数据
+                await this.loadCampusOptions();
+                // 设置默认值
+                this.setDefaultValues();
+
+                // 编辑模式加载数据
+                if (this.pageType === 'update' && this.checkId) {
+                    await this.loadCheckData();
+                }
+            } catch (error) {
+                console.error('初始化页面失败:', error);
+                uni.showToast({
+                    title: '页面初始化失败',
+                    icon: 'none',
+                });
+            } finally {
+                this.pageLoading = false;
+            }
+        },
+
+        // 加载字典数据
+        async loadDictionaries() {
+            try {
+                const dictTypes = [
+                    'xg_dorm_hygiene_check_type',
+                    'xg_dorm_hygiene_check_level',
+                    'xg_dorm_hygiene_grade',
+                    'xg_student_attendance_status',
+                ];
+
+                const results = await Promise.all(dictTypes.map((type) => getDict(type)));
+
+                this.xg_dorm_hygiene_check_type = results[0] || [];
+                this.xg_dorm_hygiene_check_level = results[1] || [];
+                this.xg_dorm_hygiene_grade = results[2] || [];
+                this.xg_student_attendance_status = results[3] || [];
+
+                console.log('字典数据加载完成');
+            } catch (error) {
+                console.error('加载字典数据失败:', error);
+            }
+        },
+
+        // 设置默认值
+        setDefaultValues() {
+            const now = new Date();
+            this.checkDateValue = now.getTime();
+            this.checkDateDisplay = this.formatDateTime(now);
+            this.formData.checkDate = this.formatDateTime(now);
+
+            // 设置默认检查标题
+            const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
+            this.formData.checkTitle = `${dateStr}宿舍卫生检查`;
+
+            // 获取当前用户信息
+            this.getCurrentUserInfo();
+        },
+
+        // 获取当前用户信息
+        async getCurrentUserInfo() {
+            try {
+                const res = await request({
+                    url: '/system/user/getInfo',
+                    method: 'GET',
+                });
+
+                if (res && res.code === 200 && res.data && res.data.user) {
+                    const user = res.data.user;
+                    this.formData.checkerId = user.userId;
+                    this.formData.checkerName = user.nickName || user.userName;
+                }
+            } catch (error) {
+                console.warn('获取用户信息失败:', error);
+            }
+        },
+
+        // 加载检查数据(编辑模式)
+        async loadCheckData() {
+            try {
+                const res = await request({
+                    url: `/dormHygieneCheck/dormHygieneCheck/${this.checkId}`,
+                    method: 'GET',
+                });
+
+                if (res && res.code === 200 && res.data) {
+                    Object.assign(this.formData, res.data);
+
+                    // 设置时间显示
+                    if (this.formData.checkDate) {
+                        const date = new Date(this.formData.checkDate);
+                        this.checkDateValue = date.getTime();
+                        this.checkDateDisplay = this.formatDateTime(date);
+                    }
+
+                    // 加载级联数据
+                    await this.loadCascadeData();
+                    // 加载房间学生数据
+                    await this.loadRoomStudents();
+                    // 加载附件数据
+                    await this.loadAttachmentData();
+                }
+            } catch (error) {
+                console.error('加载检查数据失败:', error);
+                uni.showToast({
+                    title: '加载数据失败',
+                    icon: 'none',
+                });
+            }
+        },
+
+        // 加载级联数据
+        async loadCascadeData() {
+            try {
+                const tasks = [];
+
+                if (this.formData.campusId) {
+                    tasks.push(this.loadAreaOptions(this.formData.campusId));
+                }
+                if (this.formData.areaId) {
+                    tasks.push(this.loadBuildingOptions(this.formData.areaId));
+                }
+                if (this.formData.buildingId) {
+                    tasks.push(this.loadRoomOptions(this.formData.buildingId));
+                }
+
+                await Promise.all(tasks);
+            } catch (error) {
+                console.error('加载级联数据失败:', error);
+            }
+        },
+
+        // 加载校区选项
+        async loadCampusOptions() {
+            try {
+                const res = await request({
+                    url: '/xqxx/dormCampus/list',
+                    method: 'GET',
+                });
+                this.campusOptions = res.rows || [];
+            } catch (error) {
+                console.error('加载校区选项失败:', error);
+            }
+        },
+
+        // 加载宿舍区选项
+        async loadAreaOptions(campusId) {
+            try {
+                const res = await request({
+                    url: '/ssqxx/dormArea/list',
+                    method: 'GET',
+                    params: { campusId },
+                });
+                this.areaOptions = res.rows || [];
+            } catch (error) {
+                console.error('加载宿舍区选项失败:', error);
+            }
+        },
+
+        // 加载宿舍楼选项
+        async loadBuildingOptions(areaId) {
+            try {
+                const res = await request({
+                    url: '/sslxx/dormBuilding/list',
+                    method: 'GET',
+                    params: { areaId },
+                });
+                this.buildingOptions = res.rows || [];
+            } catch (error) {
+                console.error('加载宿舍楼选项失败:', error);
+            }
+        },
+
+        // 加载房间选项
+        async loadRoomOptions(buildingId) {
+            try {
+                const res = await request({
+                    url: '/xgss/dormRoom/list',
+                    method: 'GET',
+                    params: { buildingId },
+                });
+                this.roomOptions = res.rows || [];
+            } catch (error) {
+                console.error('加载房间选项失败:', error);
+            }
+        },
+
+        // 加载房间学生信息
+        async loadRoomStudents() {
+            if (!this.formData.roomId) {
+                this.roomStudents = [];
+                return;
+            }
+
+            try {
+                // 如果是编辑模式,先尝试获取历史评分数据
+                if (this.pageType === 'update' && this.formData.id) {
+                    const studentScoresRes = await request({
+                        url: `/dormHygieneStudentScore/dormHygieneStudentScore/getByCheckId/${this.formData.id}`,
+                        method: 'GET',
+                    });
+
+                    if (
+                        studentScoresRes.code === 200 &&
+                        studentScoresRes.data &&
+                        studentScoresRes.data.length > 0
+                    ) {
+                        this.roomStudents = studentScoresRes.data.map((item) => ({
+                            ...item,
+                            student_score: item.studentScore || null,
+                            attendance_status: item.attendanceStatus || '1',
+                            student_grade: item.studentGrade || 'A',
+                            showAttendancePicker: false,
+                            showGradePicker: false,
+                        }));
+                        return;
+                    }
+                }
+
+                // 获取房间学生基础数据
+                const res = await request({
+                    url: '/dormBedStudent/dormBedStudent/getRoomStudents',
+                    method: 'GET',
+                    params: { roomId: this.formData.roomId },
+                });
+
+                if (res.code === 200 && res.data) {
+                    this.roomStudents = res.data.map((item) => ({
+                        ...item,
+                        student_score: null,
+                        attendance_status: '1',
+                        student_grade: 'A',
+                        showAttendancePicker: false,
+                        showGradePicker: false,
+                    }));
+                }
+            } catch (error) {
+                console.error('加载房间学生信息失败:', error);
+            }
+        },
+
+        // 加载附件数据
+        async loadAttachmentData() {
+            if (!this.formData.attachmentIds) {
+                this.imageList = [];
+                this.documentList = [];
+                return;
+            }
+
+            try {
+                const ossIds = this.formData.attachmentIds.split(',').filter((id) => id.trim());
+                if (ossIds.length === 0) return;
+
+                const res = await request({
+                    url: `/resource/oss/listByIds/${ossIds.join(',')}`,
+                    method: 'GET',
+                });
+
+                if (res.code === 200 && res.data) {
+                    const images = [];
+                    const documents = [];
+
+                    res.data.forEach((item) => {
+                        const isImage = /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(item.originalName);
+                        if (isImage) {
+                            images.push({
+                                url: item.url,
+                                ossId: item.ossId,
+                                name: item.originalName,
+                            });
+                        } else {
+                            documents.push({
+                                url: item.url,
+                                ossId: item.ossId,
+                                name: item.originalName,
+                            });
+                        }
+                    });
+
+                    this.imageList = images;
+                    this.documentList = documents;
+                }
+            } catch (error) {
+                console.error('加载附件数据失败:', error);
+            }
+        },
+
+        // 时间选择确认
+        handleDateConfirm(e) {
+            this.checkDateValue = e.value;
+            const date = new Date(e.value);
+            this.checkDateDisplay = this.formatDateTime(date);
+            this.formData.checkDate = this.formatDateTime(date);
+            this.showDatePicker = false;
+        },
+
+        // 校区选择确认
+        async handleCampusConfirm(e) {
+            const campus = this.campusOptions.find((item) => item.id === e.value[0]);
+            if (campus) {
+                this.formData.campusId = campus.id;
+                this.formData.campusName = campus.campusName;
+
+                // 清空下级选项
+                this.clearSubOptions('campus');
+                await this.loadAreaOptions(campus.id);
+            }
+            this.showCampusPicker = false;
+        },
+
+        // 宿舍区选择确认
+        async handleAreaConfirm(e) {
+            const area = this.areaOptions.find((item) => item.id === e.value[0]);
+            if (area) {
+                this.formData.areaId = area.id;
+                this.formData.areaName = area.areaName;
+
+                // 清空下级选项
+                this.clearSubOptions('area');
+                await this.loadBuildingOptions(area.id);
+            }
+            this.showAreaPicker = false;
+        },
+
+        // 宿舍楼选择确认
+        async handleBuildingConfirm(e) {
+            const building = this.buildingOptions.find((item) => item.id === e.value[0]);
+            if (building) {
+                this.formData.buildingId = building.id;
+                this.formData.buildingName = building.buildingName;
+
+                // 清空下级选项
+                this.clearSubOptions('building');
+                await this.loadRoomOptions(building.id);
+            }
+            this.showBuildingPicker = false;
+        },
+
+        // 房间选择确认
+        async handleRoomConfirm(e) {
+            const room = this.roomOptions.find((item) => item.id === e.value[0]);
+            if (room) {
+                this.formData.roomId = room.id;
+                this.formData.roomName = room.roomName;
+                this.formData.roomCode = room.roomCode || room.roomName;
+
+                // 加载房间学生信息
+                await this.loadRoomStudents();
+            }
+            this.showRoomPicker = false;
+        },
+
+        // 清空下级选项
+        clearSubOptions(level) {
+            switch (level) {
+                case 'campus':
+                    this.formData.areaId = undefined;
+                    this.formData.areaName = undefined;
+                    this.areaOptions = [];
+                case 'area':
+                    this.formData.buildingId = undefined;
+                    this.formData.buildingName = undefined;
+                    this.buildingOptions = [];
+                case 'building':
+                    this.formData.roomId = undefined;
+                    this.formData.roomName = undefined;
+                    this.formData.roomCode = undefined;
+                    this.roomOptions = [];
+                    this.roomStudents = [];
+                    break;
+            }
+        },
+
+        // 检查类型选择确认
+        handleCheckTypeConfirm(e) {
+            this.formData.checkType = e.value[0];
+            this.showCheckTypePicker = false;
+        },
+
+        // 检查级别选择确认
+        handleCheckLevelConfirm(e) {
+            this.formData.checkLevel = e.value[0];
+            this.showCheckLevelPicker = false;
+        },
+
+        // 卫生等级选择确认
+        handleHygieneGradeConfirm(e) {
+            this.formData.hygieneGrade = e.value[0];
+
+            // 自动为所有学生设置相同评级
+            if (this.roomStudents.length > 0) {
+                this.roomStudents.forEach((student) => {
+                    student.student_grade = e.value[0];
+                });
+
+                const gradeItem = this.xg_dorm_hygiene_grade.find((d) => d.value === e.value[0]);
+                if (gradeItem) {
+                    uni.showToast({
+                        title: `已为${this.roomStudents.length}名学生设置评级:${gradeItem.label}`,
+                        icon: 'none',
+                    });
+                }
+            }
+
+            this.showHygieneGradePicker = false;
+        },
+
+        // 总分变化处理
+        handleTotalScoreChange() {
+            const score = parseFloat(this.formData.totalScore);
+            if (!isNaN(score) && score >= 0 && score <= 100 && this.roomStudents.length > 0) {
+                this.roomStudents.forEach((student) => {
+                    student.student_score = score;
+                });
+
+                uni.showToast({
+                    title: `已为${this.roomStudents.length}名学生设置个人得分:${score}分`,
+                    icon: 'none',
+                });
+            }
+        },
+
+        // 学生分数变化处理
+        handleStudentScoreChange(student) {
+            const score = parseFloat(student.student_score);
+            if (!isNaN(score) && (score < 0 || score > 100)) {
+                uni.showToast({
+                    title: '个人得分必须在0-100分之间',
+                    icon: 'none',
+                });
+                student.student_score = null;
+            }
+        },
+
+        // 出勤状态选择确认
+        handleAttendanceConfirm(e, student) {
+            student.attendance_status = e.value[0];
+            student.showAttendancePicker = false;
+        },
+
+        // 学生评级选择确认
+        handleStudentGradeConfirm(e, student) {
+            student.student_grade = e.value[0];
+            student.showGradePicker = false;
+        },
+
+        // 获取出勤状态显示文本
+        getAttendanceDisplay(value) {
+            const item = this.xg_student_attendance_status.find((d) => d.value === value);
+            return item ? item.label : '';
+        },
+
+        // 获取评级显示文本
+        getGradeDisplay(value) {
+            const item = this.xg_dorm_hygiene_grade.find((d) => d.value === value);
+            return item ? item.label : '';
+        },
+
+        // 选择图片
+        chooseImage() {
+            uni.chooseImage({
+                count: 9 - this.imageList.length,
+                sizeType: ['compressed'],
+                sourceType: ['album', 'camera'],
+                success: (res) => {
+                    res.tempFilePaths.forEach((path) => {
+                        this.uploadFile(path, true);
+                    });
+                },
+            });
+        },
+
+        // 选择文档
+        chooseDocument() {
+            // H5环境下的文件选择
+            // #ifdef H5
+            const input = document.createElement('input');
+            input.type = 'file';
+            input.accept = '.pdf,.doc,.docx,.txt,.xls,.xlsx';
+            input.onchange = (e) => {
+                const files = e.target.files;
+                for (let i = 0; i < files.length; i++) {
+                    this.uploadFile(files[i], false);
+                }
+            };
+            input.click();
+            // #endif
+
+            // 非H5环境提示
+            // #ifndef H5
+            uni.showToast({
+                title: '该功能仅在H5环境下支持',
+                icon: 'none',
+            });
+            // #endif
+        },
+
+        // 文件上传
+        async uploadFile(fileOrPath, isImage) {
+            const formData = {
+                bizType: 'dorm_hygiene_check_attachment',
+            };
+
+            try {
+                uni.showLoading({ title: '上传中...' });
+
+                const res = await smartUploadFile('/resource/oss/upload', fileOrPath, formData);
+
+                if (res.code === 200) {
+                    const fileData = {
+                        url: res.data.url,
+                        ossId: res.data.ossId,
+                        name: res.data.originalName || '未知文件',
+                    };
+
+                    if (isImage) {
+                        this.imageList.push(fileData);
+                    } else {
+                        this.documentList.push(fileData);
+                    }
+
+                    uni.showToast({
+                        title: '上传成功',
+                        icon: 'success',
+                    });
+                } else {
+                    throw new Error(res.msg || '上传失败');
+                }
+            } catch (error) {
+                console.error('文件上传失败:', error);
+                uni.showToast({
+                    title: error.message || '上传失败',
+                    icon: 'none',
+                });
+            } finally {
+                uni.hideLoading();
+            }
+        },
+
+        // 预览图片
+        previewImage(index) {
+            const urls = this.imageList.map((item) => item.url);
+            uni.previewImage({
+                current: index,
+                urls: urls,
+            });
+        },
+
+        // 删除图片
+        removeImage(index) {
+            this.imageList.splice(index, 1);
+        },
+
+        // 下载文档
+        async downloadDocument(doc) {
+            try {
+                // H5环境下载
+                // #ifdef H5
+                const link = document.createElement('a');
+                link.href = doc.url;
+                link.download = doc.name;
+                link.click();
+                // #endif
+
+                // 非H5环境
+                // #ifndef H5
+                uni.downloadFile({
+                    url: doc.url,
+                    success: (res) => {
+                        if (res.statusCode === 200) {
+                            uni.openDocument({
+                                filePath: res.tempFilePath,
+                                success: () => {
+                                    console.log('打开文档成功');
+                                },
+                            });
+                        }
+                    },
+                });
+                // #endif
+            } catch (error) {
+                console.error('下载文档失败:', error);
+                uni.showToast({
+                    title: '下载失败',
+                    icon: 'none',
+                });
+            }
+        },
+
+        // 删除文档
+        removeDocument(index) {
+            this.documentList.splice(index, 1);
+        },
+
+        // 格式化日期时间
+        formatDateTime(date) {
+            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}`;
+        },
+
+        // 表单提交
+        async handleSubmit() {
+            try {
+                // 表单验证
+                const valid = await this.$refs.formRef.validate();
+                if (!valid) return;
+
+                this.submitLoading = true;
+
+                // 处理附件ID
+                const allAttachments = [...this.imageList, ...this.documentList];
+                this.formData.attachmentIds = allAttachments.map((item) => item.ossId).join(',');
+
+                // 处理学生评分数据
+                const studentScores = this.roomStudents.map((student) => ({
+                    id: student.id, // 编辑时包含ID
+                    studentId: student.studentId,
+                    studentName: student.studentName,
+                    studentNo: student.studentNo,
+                    className: student.className,
+                    majorName: student.majorName,
+                    collegeName: student.collegeName,
+                    bedId: student.bedId,
+                    bedCode: student.bedName,
+                    studentScore: student.student_score ? Number(student.student_score) : undefined,
+                    attendanceStatus: student.attendance_status,
+                    studentGrade: student.student_grade,
+                    isResponsiblePerson: student.isResponsiblePerson
+                        ? Number(student.isResponsiblePerson)
+                        : 0,
+                    scoreRemarks: student.scoreRemarks || '',
+                    improvementNotes: student.improvementNotes || '',
+                }));
+
+                // 构建提交数据
+                const submitData = {
+                    ...this.formData,
+                    studentScores: studentScores,
+                };
+
+                console.log('提交数据:', submitData);
+
+                // 调用API
+                const url =
+                    this.pageType === 'add'
+                        ? '/dormHygieneCheck/dormHygieneCheck'
+                        : `/dormHygieneCheck/dormHygieneCheck`;
+
+                const res = await request({
+                    url: url,
+                    method: this.pageType === 'add' ? 'POST' : 'PUT',
+                    data: submitData,
+                });
+
+                if (res && res.code === 200) {
+                    uni.showToast({
+                        title: '保存成功',
+                        icon: 'success',
+                    });
+
+                    // 发送刷新事件
+                    uni.$emit('refreshCheckList');
+
+                    // 返回上一页
+                    setTimeout(() => {
+                        uni.navigateBack();
+                    }, 1500);
+                } else {
+                    throw new Error(res?.msg || '保存失败');
+                }
+            } catch (error) {
+                console.error('提交失败:', error);
+                uni.showToast({
+                    title: error.message || '保存失败',
+                    icon: 'none',
+                });
+            } finally {
+                this.submitLoading = false;
+            }
+        },
+
+        // 返回上一页
+        goBack() {
+            uni.navigateBack();
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.page-container {
+    min-height: 100vh;
+    background-color: #f5f6fa;
+    padding-bottom: 120rpx;
+}
+
+.form-container {
+    padding: 20rpx;
+}
+
+.form-section {
+    background-color: #ffffff;
+    border-radius: 16rpx;
+    padding: 32rpx;
+    margin-bottom: 20rpx;
+    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
+}
+
+.section-title {
+    font-size: 32rpx;
+    font-weight: 600;
+    color: #303133;
+    margin-bottom: 32rpx;
+    padding-bottom: 16rpx;
+    border-bottom: 2rpx solid #e4e7ed;
+    display: block;
+}
+
+// 学生信息样式
+.student-list {
+    display: flex;
+    flex-direction: column;
+    gap: 24rpx;
+}
+
+.student-item {
+    background-color: #f8f9fa;
+    border-radius: 12rpx;
+    padding: 24rpx;
+    border: 1px solid #e9ecef;
+}
+
+.student-header {
+    display: flex;
+    align-items: center;
+    gap: 16rpx;
+    margin-bottom: 20rpx;
+    padding-bottom: 16rpx;
+    border-bottom: 1px solid #e9ecef;
+}
+
+.student-name {
+    font-size: 28rpx;
+    font-weight: 600;
+    color: #303133;
+}
+
+.student-no {
+    font-size: 24rpx;
+    color: #606266;
+    background-color: #e9ecef;
+    padding: 4rpx 8rpx;
+    border-radius: 4rpx;
+}
+
+.student-bed {
+    font-size: 24rpx;
+    color: #909399;
+    background-color: #f0f0f0;
+    padding: 4rpx 8rpx;
+    border-radius: 4rpx;
+}
+
+.student-form {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.form-row {
+    display: flex;
+    gap: 20rpx;
+}
+
+.form-col {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    gap: 8rpx;
+}
+
+.form-label {
+    font-size: 24rpx;
+    color: #606266;
+    margin-bottom: 8rpx;
+}
+
+// 附件样式
+.attachment-section {
+    margin-bottom: 32rpx;
+
+    &:last-child {
+        margin-bottom: 0;
+    }
+}
+
+.attachment-title {
+    font-size: 28rpx;
+    font-weight: 500;
+    color: #303133;
+    margin-bottom: 20rpx;
+    display: block;
+}
+
+// 图片上传样式
+.image-upload-container {
+    width: 100%;
+}
+
+.image-list {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    gap: 16rpx;
+}
+
+.image-item {
+    position: relative;
+    width: 100%;
+    aspect-ratio: 1;
+    border-radius: 12rpx;
+    overflow: hidden;
+}
+
+.image-preview {
+    width: 100%;
+    height: 100%;
+    border-radius: 12rpx;
+}
+
+.image-actions {
+    position: absolute;
+    top: 8rpx;
+    right: 8rpx;
+    background-color: rgba(0, 0, 0, 0.5);
+    border-radius: 50%;
+    width: 48rpx;
+    height: 48rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.image-upload-btn {
+    width: 100%;
+    aspect-ratio: 1;
+    border: 2rpx dashed #d9d9d9;
+    border-radius: 12rpx;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    gap: 8rpx;
+    background-color: #fafafa;
+}
+
+.upload-text {
+    font-size: 24rpx;
+    color: #909399;
+}
+
+// 文档上传样式
+.document-upload-container {
+    width: 100%;
+}
+
+.document-list {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+    margin-bottom: 20rpx;
+}
+
+.document-item {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 16rpx;
+    background-color: #f8f9fa;
+    border-radius: 8rpx;
+    border: 1px solid #e9ecef;
+}
+
+.document-info {
+    display: flex;
+    align-items: center;
+    gap: 12rpx;
+    flex: 1;
+}
+
+.document-name {
+    font-size: 26rpx;
+    color: #303133;
+    flex: 1;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.document-actions {
+    display: flex;
+    align-items: center;
+    gap: 16rpx;
+}
+
+.document-upload-btn {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 12rpx;
+    padding: 24rpx;
+    border: 2rpx dashed #d9d9d9;
+    border-radius: 12rpx;
+    background-color: #fafafa;
+}
+
+// 底部操作按钮
+.bottom-actions {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    padding: 20rpx;
+    background-color: #ffffff;
+    border-top: 1px solid #e4e7ed;
+    display: flex;
+    gap: 20rpx;
+    z-index: 999;
+}
+
+.bottom-actions .u-button {
+    flex: 1;
+}
+
+// 响应式设计
+@media (max-width: 375px) {
+    .form-container {
+        padding: 16rpx;
+    }
+
+    .form-section {
+        padding: 24rpx;
+        margin-bottom: 16rpx;
+    }
+
+    .student-item {
+        padding: 20rpx;
+    }
+
+    .form-row {
+        flex-direction: column;
+        gap: 16rpx;
+    }
+
+    .image-list {
+        grid-template-columns: repeat(2, 1fr);
+    }
+}
+
+// uview组件样式调整
+::v-deep .u-form-item {
+    margin-bottom: 32rpx;
+}
+
+::v-deep .u-input {
+    font-size: 28rpx;
+}
+
+::v-deep .u-textarea {
+    font-size: 28rpx;
+}
+
+::v-deep .u-button {
+    border-radius: 12rpx;
+}
+</style>

+ 1 - 1
web/src/views/xg/xgss/dormHygieneCheck/dormHygieneCheck/index.vue

@@ -128,7 +128,7 @@
             <el-tooltip content="删除" placement="top">
               <el-button
                 link
-                type="primary"
+                type="danger"
                 icon="Delete"
                 @click="handleDelete(scope.row)"
                 v-hasPermi="['dormHygieneCheck:dormHygieneCheck:remove']"