zlwu 1 год назад
Родитель
Сommit
4c63668c19

+ 30 - 6
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentDynamicsServiceImpl.java

@@ -135,8 +135,16 @@ public class XgStudentDynamicsServiceImpl implements IXgStudentDynamicsService {
         lqw.in(XgDormApplication::getCurrentBuildingId, managedBuildingIds);
 
         // 添加其他查询条件
-        lqw.like(bo.getStudentName() != null, XgDormApplication::getStudentName, bo.getStudentName());
-        lqw.like(bo.getStudentNo() != null, XgDormApplication::getStudentNo, bo.getStudentNo());
+        // 姓名或学号检索(OR关系)
+        if (bo.getStudentName() != null && bo.getStudentNo() != null) {
+            lqw.and(wrapper -> wrapper
+                    .like(XgDormApplication::getStudentName, bo.getStudentName())
+                    .or()
+                    .like(XgDormApplication::getStudentNo, bo.getStudentNo()));
+        } else {
+            lqw.like(bo.getStudentName() != null, XgDormApplication::getStudentName, bo.getStudentName());
+            lqw.like(bo.getStudentNo() != null, XgDormApplication::getStudentNo, bo.getStudentNo());
+        }
         lqw.like(bo.getCollegeName() != null, XgDormApplication::getCollegeName, bo.getCollegeName());
         lqw.like(bo.getMajorName() != null, XgDormApplication::getMajorName, bo.getMajorName());
         lqw.like(bo.getClassName() != null, XgDormApplication::getClassName, bo.getClassName());
@@ -169,8 +177,16 @@ public class XgStudentDynamicsServiceImpl implements IXgStudentDynamicsService {
         lqw.in(XgHolidayStay::getBuildingId, managedBuildingIds);
 
         // 添加其他查询条件
-        lqw.like(bo.getStudentName() != null, XgHolidayStay::getStudentName, bo.getStudentName());
-        lqw.like(bo.getStudentNo() != null, XgHolidayStay::getStudentNo, bo.getStudentNo());
+        // 姓名或学号检索(OR关系)
+        if (bo.getStudentName() != null && bo.getStudentNo() != null) {
+            lqw.and(wrapper -> wrapper
+                    .like(XgHolidayStay::getStudentName, bo.getStudentName())
+                    .or()
+                    .like(XgHolidayStay::getStudentNo, bo.getStudentNo()));
+        } else {
+            lqw.like(bo.getStudentName() != null, XgHolidayStay::getStudentName, bo.getStudentName());
+            lqw.like(bo.getStudentNo() != null, XgHolidayStay::getStudentNo, bo.getStudentNo());
+        }
         lqw.like(bo.getCollegeName() != null, XgHolidayStay::getCollegeName, bo.getCollegeName());
         lqw.like(bo.getMajorName() != null, XgHolidayStay::getMajorName, bo.getMajorName());
         lqw.like(bo.getClassName() != null, XgHolidayStay::getClassName, bo.getClassName());
@@ -203,8 +219,16 @@ public class XgStudentDynamicsServiceImpl implements IXgStudentDynamicsService {
         lqw.in(XgStudentLeave::getStudentId, managedStudentIds);
 
         // 添加其他查询条件
-        lqw.like(bo.getStudentName() != null, XgStudentLeave::getStudentName, bo.getStudentName());
-        lqw.like(bo.getStudentNo() != null, XgStudentLeave::getStudentNo, bo.getStudentNo());
+        // 姓名或学号检索(OR关系)
+        if (bo.getStudentName() != null && bo.getStudentNo() != null) {
+            lqw.and(wrapper -> wrapper
+                    .like(XgStudentLeave::getStudentName, bo.getStudentName())
+                    .or()
+                    .like(XgStudentLeave::getStudentNo, bo.getStudentNo()));
+        } else {
+            lqw.like(bo.getStudentName() != null, XgStudentLeave::getStudentName, bo.getStudentName());
+            lqw.like(bo.getStudentNo() != null, XgStudentLeave::getStudentNo, bo.getStudentNo());
+        }
         lqw.like(bo.getCollegeName() != null, XgStudentLeave::getCollegeName, bo.getCollegeName());
         lqw.like(bo.getMajorName() != null, XgStudentLeave::getMajorName, bo.getMajorName());
         lqw.like(bo.getClassName() != null, XgStudentLeave::getClassName, bo.getClassName());

+ 9 - 0
mobile/src/App.vue

@@ -150,6 +150,15 @@ page {
     border-color: #007dfb !important;
 }
 
+/* Tabs组件主题色覆盖 - 全局统一底部方块颜色 */
+.u-tabs__wrapper__nav__line {
+    background-color: #0051d9 !important;
+}
+
+.u-tabs .u-tabs__wrapper__nav__line {
+    background: #0051d9 !important;
+}
+
 /* ========== 全局温暖商务风格样式 ========== */
 
 /* 全局页面背景 */

+ 180 - 17
mobile/src/components/Process/approvalRecord.vue

@@ -73,18 +73,36 @@
                             <!-- 办理人信息 -->
                             <view class="approver-section">
                                 <text class="section-label">办理人:</text>
-                                <view class="approver-tags">
-                                    <template v-if="record.approveName">
-                                        <u-tag
-                                            v-for="(name, nameIndex) in record.approveName.split(
-                                                ',',
-                                            )"
-                                            :key="nameIndex"
-                                            :text="name.trim()"
-                                            :type="getApproverType()"
-                                            size="mini"
-                                            class="approver-tag"
-                                        />
+                                <view class="approver-info-list">
+                                    <template
+                                        v-if="record.approverList && record.approverList.length > 0"
+                                    >
+                                        <view
+                                            v-for="(approver, approverIndex) in record.approverList"
+                                            :key="approverIndex"
+                                            class="approver-info-item"
+                                        >
+                                            <u-tag
+                                                :text="approver.name"
+                                                :type="getApproverType()"
+                                                size="mini"
+                                                class="approver-tag"
+                                            />
+                                            <view
+                                                v-if="approver.phone"
+                                                class="approver-phone-wrapper"
+                                                @click="handleCallPhone(approver.phone)"
+                                            >
+                                                <u-icon
+                                                    name="phone-fill"
+                                                    size="28"
+                                                    color="#1989fa"
+                                                />
+                                                <text class="approver-phone">
+                                                    {{ approver.phone }}
+                                                </text>
+                                            </view>
+                                        </view>
                                     </template>
                                     <u-tag
                                         v-else
@@ -253,10 +271,43 @@ export default {
                     console.log('实例ID:', this.insId);
                     console.log('记录数量:', this.historyList.length);
 
-                    // 处理附件信息(与web端保持一致)
+                    // 处理附件信息和审批人信息(与web端保持一致)
                     if (this.historyList.length > 0) {
-                        console.log('开始处理附件信息...');
+                        console.log('开始处理附件信息和审批人信息...');
+
+                        // 收集所有审批人ID
+                        const allApproverIds = new Set();
+                        for (const item of this.historyList) {
+                            if (item.approver) {
+                                // approver字段可能包含多个ID,用逗号分隔
+                                const ids = item.approver
+                                    .split(',')
+                                    .map((id) => id.trim())
+                                    .filter((id) => id);
+                                ids.forEach((id) => allApproverIds.add(id));
+                            }
+                        }
+
+                        // 批量查询审批人信息(包含手机号码)
+                        let userInfoMap = {};
+                        if (allApproverIds.size > 0) {
+                            console.log(
+                                '开始批量查询审批人信息,ID列表:',
+                                Array.from(allApproverIds),
+                            );
+                            try {
+                                userInfoMap = await this.getUserInfoByIds(
+                                    Array.from(allApproverIds),
+                                );
+                                console.log('审批人信息查询完成:', userInfoMap);
+                            } catch (error) {
+                                console.error('批量查询审批人信息失败:', error);
+                            }
+                        }
+
+                        // 处理每条审批记录
                         for (const item of this.historyList) {
+                            // 处理附件信息
                             if (item.ext) {
                                 try {
                                     const attachmentRes = await this.getAttachmentsByIds(item.ext);
@@ -268,8 +319,31 @@ export default {
                             } else {
                                 item.attachmentList = [];
                             }
+
+                            // 处理审批人信息(包含姓名和手机号)
+                            if (item.approver && item.approveName) {
+                                const approverIds = item.approver
+                                    .split(',')
+                                    .map((id) => id.trim())
+                                    .filter((id) => id);
+                                const approverNames = item.approveName
+                                    .split(',')
+                                    .map((name) => name.trim())
+                                    .filter((name) => name);
+
+                                item.approverList = approverIds.map((id, index) => {
+                                    const userInfo = userInfoMap[id];
+                                    return {
+                                        id: id,
+                                        name: approverNames[index] || '未知',
+                                        phone: userInfo ? userInfo.phonenumber : null,
+                                    };
+                                });
+                            } else {
+                                item.approverList = [];
+                            }
                         }
-                        console.log('附件信息处理完成');
+                        console.log('附件信息和审批人信息处理完成');
                     }
                 } else {
                     console.error('API响应异常:', response);
@@ -300,6 +374,67 @@ export default {
             return response;
         },
 
+        // 批量查询用户信息(包含手机号码)
+        async getUserInfoByIds(userIds) {
+            try {
+                const response = await request({
+                    url: '/system/user/optionselect',
+                    method: 'GET',
+                    params: {
+                        userIds: userIds.join(','),
+                    },
+                });
+
+                if (response.code === 200 && response.data) {
+                    // 将数组转换为以userId为key的Map,方便后续查找
+                    const userMap = {};
+                    response.data.forEach((user) => {
+                        userMap[user.userId] = user;
+                    });
+                    return userMap;
+                } else {
+                    console.error('查询用户信息失败:', response.msg);
+                    return {};
+                }
+            } catch (error) {
+                console.error('批量查询用户信息异常:', error);
+                return {};
+            }
+        },
+
+        // 拨打电话
+        handleCallPhone(phoneNumber) {
+            if (!phoneNumber) {
+                uni.showToast({
+                    title: '手机号码为空',
+                    icon: 'none',
+                });
+                return;
+            }
+
+            uni.showModal({
+                title: '拨打电话',
+                content: `确定要拨打 ${phoneNumber} 吗?`,
+                success: (res) => {
+                    if (res.confirm) {
+                        uni.makePhoneCall({
+                            phoneNumber: phoneNumber,
+                            success: () => {
+                                console.log('拨打电话成功');
+                            },
+                            fail: (err) => {
+                                console.error('拨打电话失败:', err);
+                                uni.showToast({
+                                    title: '拨打电话失败',
+                                    icon: 'none',
+                                });
+                            },
+                        });
+                    }
+                },
+            });
+        },
+
         // 获取状态样式类(使用字典方式)
         getStatusClass(flowStatus) {
             console.log('flowStatus:', flowStatus);
@@ -670,16 +805,44 @@ export default {
     display: block;
 }
 
-.approver-tags {
+.approver-info-list {
     display: flex;
+    flex-direction: column;
+    gap: 12rpx;
+}
+
+.approver-info-item {
+    display: flex;
+    align-items: center;
+    gap: 12rpx;
     flex-wrap: wrap;
-    gap: 8rpx;
 }
 
 .approver-tag {
     margin: 0;
 }
 
+.approver-phone-wrapper {
+    display: flex;
+    align-items: center;
+    gap: 6rpx;
+    padding: 4rpx 8rpx;
+    border-radius: 8rpx;
+    transition: all 0.3s;
+    cursor: pointer;
+}
+
+.approver-phone-wrapper:active {
+    background-color: #e6f7ff;
+    transform: scale(0.98);
+}
+
+.approver-phone {
+    font-size: 26rpx;
+    color: #1989fa;
+    font-weight: 500;
+}
+
 .comment-text {
     font-size: 26rpx;
     color: #333;

+ 8 - 0
mobile/src/pages.json

@@ -333,6 +333,14 @@
 				"navigationBarBackgroundColor": "#0051d9",
 				"navigationBarTextStyle": "white"
 			}
+		},
+		{
+			"path": "pages/xg/studentDynamics/index",
+			"style": {
+				"navigationBarTitleText": "学生动向信息",
+				"navigationBarBackgroundColor": "#0051d9",
+				"navigationBarTextStyle": "white"
+			}
 		}
 	],
 	"globalStyle": {

+ 22 - 0
mobile/src/pages/index/index.vue

@@ -221,6 +221,20 @@
                         </template>
                     </ServiceItem>
 
+                    <ServiceItem
+                        v-if="hasPermi(['student:dynamics:list'])"
+                        title="学生动向信息(宿管)"
+                        @click="gotoStudentDynamics"
+                    >
+                        <template #icon>
+                            <image
+                                src="/static/0011.png"
+                                mode="aspectFit"
+                                style="width: 24px; height: 24px"
+                            />
+                        </template>
+                    </ServiceItem>
+
                     <ServiceItem
                         v-if="hasPermi(['student:workstudy:list'])"
                         title="勤工俭学"
@@ -722,6 +736,7 @@ export default {
                 this.hasPermi(['student:dormviolation:listapp']) ||
                 this.hasPermi(['student:visit:list']) ||
                 this.hasPermi(['student:dormhygiene:listapp']) ||
+                this.hasPermi(['student:dynamics:list']) ||
                 this.hasPermi(['student:workstudy:list']) ||
                 this.hasPermi(['student:violation:listapp']) ||
                 this.hasPermi(['student:assessment:applyapp']) ||
@@ -988,6 +1003,13 @@ export default {
             });
         },
 
+        // 跳转到学生动向信息(宿管)页面
+        gotoStudentDynamics() {
+            uni.navigateTo({
+                url: '/pages/xg/studentDynamics/index',
+            });
+        },
+
         // 跳转到修改密码页面
         gotoResetPassword() {
             uni.navigateTo({

+ 432 - 0
mobile/src/pages/xg/studentDynamics/components/DormApplicationTab.vue

@@ -0,0 +1,432 @@
+<template>
+    <view class="dorm-application-tab">
+        <!-- 搜索区域 -->
+        <view class="search-container">
+            <u-search
+                v-model="searchKeyword"
+                placeholder="请输入姓名或学号"
+                :show-action="true"
+                action-text="搜索"
+                :animation="true"
+                @search="handleQuery"
+                @custom="handleQuery"
+                @clear="handleClear"
+            ></u-search>
+        </view>
+
+        <!-- 加载状态 -->
+        <u-loading-page
+            :loading="loading && dataList.length === 0"
+            loading-text="加载中..."
+        ></u-loading-page>
+
+        <!-- 列表内容 -->
+        <view v-if="!loading || dataList.length > 0" class="content-wrapper">
+            <!-- 空状态 -->
+            <view v-if="dataList.length === 0" class="empty-container">
+                <u-empty text="暂无调宿/退宿申请记录" mode="data"></u-empty>
+            </view>
+
+            <!-- 列表 -->
+            <scroll-view
+                v-else
+                scroll-y
+                class="scroll-view"
+                :lower-threshold="50"
+                @scrolltolower="handleLoadMore"
+            >
+                <view class="list-container">
+                    <view
+                        v-for="(item, index) in dataList"
+                        :key="item.id || index"
+                        class="list-card"
+                    >
+                        <!-- 卡片头部 -->
+                        <view class="card-header">
+                            <view class="student-info">
+                                <text class="student-name">{{ item.studentName || '-' }}</text>
+                                <text class="student-no">{{ item.studentNo || '-' }}</text>
+                            </view>
+                            <u-tag
+                                :text="getApplicationTypeText(item.applicationType)"
+                                :type="getApplicationTypeTagType(item.applicationType)"
+                                size="mini"
+                            ></u-tag>
+                        </view>
+
+                        <!-- 卡片内容 -->
+                        <view class="card-content">
+                            <view class="info-row">
+                                <text class="label">学院:</text>
+                                <text class="value">{{ item.collegeName || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">专业:</text>
+                                <text class="value">{{ item.majorName || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">班级:</text>
+                                <text class="value">{{ item.className || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">当前宿舍:</text>
+                                <text class="value">
+                                    {{ item.currentBuildingName }}{{ item.currentRoomNo
+                                    }}{{ item.currentBedNo }}
+                                </text>
+                            </view>
+                            <view v-if="item.applicationType === '1'" class="info-row">
+                                <text class="label">目标宿舍:</text>
+                                <text class="value">
+                                    {{ item.targetBuildingName }}{{ item.targetRoomNo
+                                    }}{{ item.targetBedNo }}
+                                </text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">审批状态:</text>
+                                <u-tag
+                                    :text="getApprovalStatusText(item.applicationStatus)"
+                                    :type="getApprovalStatusTagType(item.applicationStatus)"
+                                    size="mini"
+                                ></u-tag>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">申请时间:</text>
+                                <text class="value">{{ formatTime(item.applyTime) }}</text>
+                            </view>
+                        </view>
+
+                        <!-- 操作按钮 -->
+                        <view class="card-actions">
+                            <u-button
+                                type="primary"
+                                size="small"
+                                plain
+                                @click="handleViewApproval(item)"
+                            >
+                                <u-icon
+                                    name="list"
+                                    size="16"
+                                    color="#fff"
+                                    style="margin-right: 4px"
+                                ></u-icon>
+                                审批记录
+                            </u-button>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 加载更多提示 -->
+                <view class="load-more-wrapper">
+                    <u-loadmore :status="loadMoreStatus" :load-text="loadMoreText" />
+                </view>
+            </scroll-view>
+        </view>
+
+        <!-- 审批记录组件 -->
+        <approval-record ref="approvalRecordRef"></approval-record>
+    </view>
+</template>
+
+<script>
+import { request } from '@/common/request.js';
+import { getDict, getDictLabel, getDictTagType } from '@/common/dict.js';
+import ApprovalRecord from '@/components/Process/approvalRecord.vue';
+
+export default {
+    name: 'DormApplicationTab',
+    components: {
+        ApprovalRecord,
+    },
+    data() {
+        return {
+            loading: false,
+            dataList: [],
+            total: 0,
+            searchKeyword: '', // 搜索关键字(姓名或学号)
+
+            // 查询参数
+            queryParams: {
+                pageNum: 1,
+                pageSize: 10,
+                studentName: '',
+                studentNo: '',
+            },
+
+            // 加载更多状态
+            loadMoreStatus: 'loadmore',
+            loadMoreText: {
+                loadmore: '点击加载更多',
+                loading: '正在加载...',
+                nomore: '没有更多了',
+            },
+
+            // 字典数据
+            dictApplicationType: [],
+            dictApprovalStatus: [],
+        };
+    },
+
+    async created() {
+        await this.loadDictData();
+        await this.getList();
+    },
+
+    methods: {
+        // 加载字典数据
+        async loadDictData() {
+            try {
+                this.dictApplicationType = await getDict('dorm_application_type');
+                this.dictApprovalStatus = await getDict('student_leave_approval_status');
+            } catch (error) {
+                console.error('加载字典数据失败:', error);
+            }
+        },
+
+        // 获取列表数据
+        async getList() {
+            this.loading = true;
+            try {
+                // 根据搜索关键字设置查询参数
+                const params = {
+                    ...this.queryParams,
+                    studentName: this.searchKeyword,
+                    studentNo: this.searchKeyword,
+                };
+
+                const res = await request({
+                    url: '/studentDynamics/dormApplicationList',
+                    method: 'GET',
+                    params: params,
+                });
+
+                if (res && res.code === 200) {
+                    if (this.queryParams.pageNum === 1) {
+                        this.dataList = res.rows || [];
+                    } else {
+                        this.dataList = [...this.dataList, ...(res.rows || [])];
+                    }
+                    this.total = res.total || 0;
+
+                    // 更新加载更多状态
+                    if (this.dataList.length >= this.total) {
+                        this.loadMoreStatus = 'nomore';
+                    } else {
+                        this.loadMoreStatus = 'loadmore';
+                    }
+                } else {
+                    uni.showToast({
+                        title: res?.msg || '获取数据失败',
+                        icon: 'none',
+                    });
+                }
+            } catch (error) {
+                console.error('获取调宿退宿申请列表失败:', error);
+                uni.showToast({
+                    title: '获取数据失败',
+                    icon: 'none',
+                });
+            } finally {
+                this.loading = false;
+            }
+        },
+
+        // 加载更多
+        handleLoadMore() {
+            if (this.loadMoreStatus === 'nomore' || this.loadMoreStatus === 'loading') {
+                return;
+            }
+
+            this.loadMoreStatus = 'loading';
+            this.queryParams.pageNum++;
+            this.getList();
+        },
+
+        // 搜索操作
+        handleQuery() {
+            this.queryParams.pageNum = 1;
+            this.dataList = [];
+            this.getList();
+        },
+
+        // 清空搜索
+        handleClear() {
+            this.searchKeyword = '';
+            this.handleQuery();
+        },
+
+        // 格式化时间
+        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;
+            }
+        },
+
+        // 获取申请类型文本
+        getApplicationTypeText(value) {
+            return getDictLabel(this.dictApplicationType, value) || '-';
+        },
+
+        // 获取申请类型标签类型
+        getApplicationTypeTagType(value) {
+            const fallbackMap = {
+                1: 'primary', // 调宿
+                2: 'warning', // 退宿
+            };
+            return getDictTagType(this.dictApplicationType, value, fallbackMap);
+        },
+
+        // 获取审批状态文本
+        getApprovalStatusText(value) {
+            return getDictLabel(this.dictApprovalStatus, value) || '-';
+        },
+
+        // 获取审批状态标签类型
+        getApprovalStatusTagType(value) {
+            const fallbackMap = {
+                0: 'warning', // 待审批
+                1: 'success', // 已通过
+                2: 'error', // 已驳回
+            };
+            return getDictTagType(this.dictApprovalStatus, value, fallbackMap);
+        },
+
+        // 查看审批记录
+        handleViewApproval(item) {
+            console.log('查看审批记录:', item);
+            if (!item.id) {
+                uni.showToast({
+                    title: '数据异常,无法查看审批记录',
+                    icon: 'none',
+                });
+                return;
+            }
+            // 调用审批记录组件,传入业务ID
+            this.$refs.approvalRecordRef.init(item.id);
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.dorm-application-tab {
+    background-color: #f5f6fa;
+    min-height: calc(100vh - 176rpx);
+}
+
+.search-container {
+    padding: 20rpx;
+    background-color: #fff;
+    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+}
+
+.content-wrapper {
+    padding: 20rpx;
+}
+
+.empty-container {
+    padding: 100rpx 20rpx;
+}
+
+.scroll-view {
+    height: calc(100vh - 176rpx - 88rpx); // 减去搜索栏高度
+}
+
+.list-container {
+    display: flex;
+    flex-direction: column;
+    gap: 20rpx;
+    padding-bottom: 20rpx;
+}
+
+.list-card {
+    background-color: #fff;
+    border-radius: 16rpx;
+    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
+    overflow: hidden;
+}
+
+.card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 24rpx;
+    border-bottom: 1px solid #f0f0f0;
+}
+
+.student-info {
+    display: flex;
+    align-items: center;
+    gap: 16rpx;
+}
+
+.student-name {
+    font-size: 32rpx;
+    font-weight: bold;
+    color: #303133;
+}
+
+.student-no {
+    font-size: 26rpx;
+    color: #909399;
+}
+
+.card-content {
+    padding: 24rpx;
+}
+
+.info-row {
+    display: flex;
+    align-items: center;
+    margin-bottom: 16rpx;
+
+    &:last-child {
+        margin-bottom: 0;
+    }
+}
+
+.label {
+    font-size: 28rpx;
+    color: #606266;
+    min-width: 140rpx;
+    margin-right: 16rpx;
+}
+
+.value {
+    font-size: 28rpx;
+    color: #303133;
+    flex: 1;
+    word-break: break-all;
+}
+
+.card-actions {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    padding: 20rpx 24rpx;
+    border-top: 1px solid #f0f0f0;
+}
+
+.load-more-wrapper {
+    padding: 20rpx 0;
+}
+
+// uview组件样式调整
+::v-deep .u-search {
+    background-color: #f5f6fa;
+}
+</style>

+ 392 - 0
mobile/src/pages/xg/studentDynamics/components/HolidayStayTab.vue

@@ -0,0 +1,392 @@
+<template>
+    <view class="holiday-stay-tab">
+        <!-- 搜索区域 -->
+        <view class="search-container">
+            <u-search
+                v-model="searchKeyword"
+                placeholder="请输入姓名或学号"
+                :show-action="true"
+                action-text="搜索"
+                :animation="true"
+                @search="handleQuery"
+                @custom="handleQuery"
+                @clear="handleClear"
+            ></u-search>
+        </view>
+
+        <!-- 加载状态 -->
+        <u-loading-page
+            :loading="loading && dataList.length === 0"
+            loading-text="加载中..."
+        ></u-loading-page>
+
+        <!-- 列表内容 -->
+        <view v-if="!loading || dataList.length > 0" class="content-wrapper">
+            <!-- 空状态 -->
+            <view v-if="dataList.length === 0" class="empty-container">
+                <u-empty text="暂无假期留宿记录" mode="data"></u-empty>
+            </view>
+
+            <!-- 列表 -->
+            <scroll-view
+                v-else
+                scroll-y
+                class="scroll-view"
+                :lower-threshold="50"
+                @scrolltolower="handleLoadMore"
+            >
+                <view class="list-container">
+                    <view
+                        v-for="(item, index) in dataList"
+                        :key="item.id || index"
+                        class="list-card"
+                    >
+                        <!-- 卡片头部 -->
+                        <view class="card-header">
+                            <view class="student-info">
+                                <text class="student-name">{{ item.studentName || '-' }}</text>
+                                <text class="student-no">{{ item.studentNo || '-' }}</text>
+                            </view>
+                            <u-tag
+                                :text="getHolidayTypeText(item.holidayType)"
+                                :type="getHolidayTypeTagType(item.holidayType)"
+                                size="mini"
+                            ></u-tag>
+                        </view>
+
+                        <!-- 卡片内容 -->
+                        <view class="card-content">
+                            <view class="info-row">
+                                <text class="label">学院:</text>
+                                <text class="value">{{ item.collegeName || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">专业:</text>
+                                <text class="value">{{ item.majorName || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">班级:</text>
+                                <text class="value">{{ item.className || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">宿舍信息:</text>
+                                <text class="value">
+                                    {{ item.buildingName }}{{ item.roomName }}
+                                </text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">留宿时间:</text>
+                                <text class="value">
+                                    {{ item.applyStartDate }} 至 {{ item.applyEndDate }}
+                                </text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">留宿原因:</text>
+                                <u-tag
+                                    :text="getStayReasonTypeText(item.stayReasonType)"
+                                    :type="getStayReasonTypeTagType(item.stayReasonType)"
+                                    size="mini"
+                                ></u-tag>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">联系电话:</text>
+                                <text class="value">{{ item.studentPhone || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">紧急联系人:</text>
+                                <text class="value">{{ item.emergencyContact || '-' }}</text>
+                            </view>
+                        </view>
+
+                        <!-- 操作按钮 - 暂不开发
+                        <view class="card-actions">
+                            <u-button
+                                type="primary"
+                                size="small"
+                                @click="handleView(item)"
+                            >查看</u-button>
+                        </view>
+                        -->
+                    </view>
+                </view>
+
+                <!-- 加载更多提示 -->
+                <view class="load-more-wrapper">
+                    <u-loadmore :status="loadMoreStatus" :load-text="loadMoreText" />
+                </view>
+            </scroll-view>
+        </view>
+    </view>
+</template>
+
+<script>
+import { request } from '@/common/request.js';
+import { getDict, getDictLabel, getDictTagType } from '@/common/dict.js';
+
+export default {
+    name: 'HolidayStayTab',
+    data() {
+        return {
+            loading: false,
+            dataList: [],
+            total: 0,
+            searchKeyword: '', // 搜索关键字(姓名或学号)
+
+            // 查询参数
+            queryParams: {
+                pageNum: 1,
+                pageSize: 10,
+                studentName: '',
+                studentNo: '',
+            },
+
+            // 加载更多状态
+            loadMoreStatus: 'loadmore',
+            loadMoreText: {
+                loadmore: '点击加载更多',
+                loading: '正在加载...',
+                nomore: '没有更多了',
+            },
+
+            // 字典数据
+            dictHolidayType: [],
+            dictStayReasonType: [],
+        };
+    },
+
+    async created() {
+        await this.loadDictData();
+        await this.getList();
+    },
+
+    methods: {
+        // 加载字典数据
+        async loadDictData() {
+            try {
+                this.dictHolidayType = await getDict('xg_holiday_type');
+                this.dictStayReasonType = await getDict('xg_stay_reason_type');
+            } catch (error) {
+                console.error('加载字典数据失败:', error);
+            }
+        },
+
+        // 获取列表数据
+        async getList() {
+            this.loading = true;
+            try {
+                // 根据搜索关键字设置查询参数
+                const params = {
+                    ...this.queryParams,
+                    studentName: this.searchKeyword,
+                    studentNo: this.searchKeyword,
+                };
+
+                const res = await request({
+                    url: '/studentDynamics/holidayStayList',
+                    method: 'GET',
+                    params: params,
+                });
+
+                if (res && res.code === 200) {
+                    if (this.queryParams.pageNum === 1) {
+                        this.dataList = res.rows || [];
+                    } else {
+                        this.dataList = [...this.dataList, ...(res.rows || [])];
+                    }
+                    this.total = res.total || 0;
+
+                    // 更新加载更多状态
+                    if (this.dataList.length >= this.total) {
+                        this.loadMoreStatus = 'nomore';
+                    } else {
+                        this.loadMoreStatus = 'loadmore';
+                    }
+                } else {
+                    uni.showToast({
+                        title: res?.msg || '获取数据失败',
+                        icon: 'none',
+                    });
+                }
+            } catch (error) {
+                console.error('获取假期留宿信息列表失败:', error);
+                uni.showToast({
+                    title: '获取数据失败',
+                    icon: 'none',
+                });
+            } finally {
+                this.loading = false;
+            }
+        },
+
+        // 加载更多
+        handleLoadMore() {
+            if (this.loadMoreStatus === 'nomore' || this.loadMoreStatus === 'loading') {
+                return;
+            }
+
+            this.loadMoreStatus = 'loading';
+            this.queryParams.pageNum++;
+            this.getList();
+        },
+
+        // 搜索操作
+        handleQuery() {
+            this.queryParams.pageNum = 1;
+            this.dataList = [];
+            this.getList();
+        },
+
+        // 清空搜索
+        handleClear() {
+            this.searchKeyword = '';
+            this.handleQuery();
+        },
+
+        // 获取假期类型文本
+        getHolidayTypeText(value) {
+            return getDictLabel(this.dictHolidayType, value) || '-';
+        },
+
+        // 获取假期类型标签类型
+        getHolidayTypeTagType(value) {
+            const fallbackMap = {
+                1: 'primary', // 寒假
+                2: 'warning', // 暑假
+                3: 'success', // 国庆
+                4: 'info', // 其他
+            };
+            return getDictTagType(this.dictHolidayType, value, fallbackMap);
+        },
+
+        // 获取留宿原因类型文本
+        getStayReasonTypeText(value) {
+            return getDictLabel(this.dictStayReasonType, value) || '-';
+        },
+
+        // 获取留宿原因类型标签类型
+        getStayReasonTypeTagType(value) {
+            const fallbackMap = {
+                1: 'primary', // 学习
+                2: 'warning', // 实习
+                3: 'success', // 工作
+                4: 'info', // 其他
+            };
+            return getDictTagType(this.dictStayReasonType, value, fallbackMap);
+        },
+
+        // 查看详情 - 暂不开发
+        // handleView(row) {
+        //     console.log('查看详情:', row);
+        // },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.holiday-stay-tab {
+    background-color: #f5f6fa;
+    min-height: calc(100vh - 176rpx);
+}
+
+.search-container {
+    padding: 20rpx;
+    background-color: #fff;
+    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+}
+
+.content-wrapper {
+    padding: 20rpx;
+}
+
+.empty-container {
+    padding: 100rpx 20rpx;
+}
+
+.scroll-view {
+    height: calc(100vh - 176rpx - 88rpx); // 减去搜索栏高度
+}
+
+.list-container {
+    display: flex;
+    flex-direction: column;
+    gap: 20rpx;
+    padding-bottom: 20rpx;
+}
+
+.list-card {
+    background-color: #fff;
+    border-radius: 16rpx;
+    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
+    overflow: hidden;
+}
+
+.card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 24rpx;
+    border-bottom: 1px solid #f0f0f0;
+}
+
+.student-info {
+    display: flex;
+    align-items: center;
+    gap: 16rpx;
+}
+
+.student-name {
+    font-size: 32rpx;
+    font-weight: bold;
+    color: #303133;
+}
+
+.student-no {
+    font-size: 26rpx;
+    color: #909399;
+}
+
+.card-content {
+    padding: 24rpx;
+}
+
+.info-row {
+    display: flex;
+    align-items: center;
+    margin-bottom: 16rpx;
+
+    &:last-child {
+        margin-bottom: 0;
+    }
+}
+
+.label {
+    font-size: 28rpx;
+    color: #606266;
+    min-width: 140rpx;
+    margin-right: 16rpx;
+}
+
+.value {
+    font-size: 28rpx;
+    color: #303133;
+    flex: 1;
+    word-break: break-all;
+}
+
+.card-actions {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    padding: 20rpx 24rpx;
+    border-top: 1px solid #f0f0f0;
+}
+
+.load-more-wrapper {
+    padding: 20rpx 0;
+}
+
+// uview组件样式调整
+::v-deep .u-search {
+    background-color: #f5f6fa;
+}
+</style>

+ 435 - 0
mobile/src/pages/xg/studentDynamics/components/StudentLeaveTab.vue

@@ -0,0 +1,435 @@
+<template>
+    <view class="student-leave-tab">
+        <!-- 搜索区域 -->
+        <view class="search-container">
+            <u-search
+                v-model="searchKeyword"
+                placeholder="请输入姓名或学号"
+                :show-action="true"
+                action-text="搜索"
+                :animation="true"
+                @search="handleQuery"
+                @custom="handleQuery"
+                @clear="handleClear"
+            ></u-search>
+        </view>
+
+        <!-- 加载状态 -->
+        <u-loading-page
+            :loading="loading && dataList.length === 0"
+            loading-text="加载中..."
+        ></u-loading-page>
+
+        <!-- 列表内容 -->
+        <view v-if="!loading || dataList.length > 0" class="content-wrapper">
+            <!-- 空状态 -->
+            <view v-if="dataList.length === 0" class="empty-container">
+                <u-empty text="暂无学生请假记录" mode="data"></u-empty>
+            </view>
+
+            <!-- 列表 -->
+            <scroll-view
+                v-else
+                scroll-y
+                class="scroll-view"
+                :lower-threshold="50"
+                @scrolltolower="handleLoadMore"
+            >
+                <view class="list-container">
+                    <view
+                        v-for="(item, index) in dataList"
+                        :key="item.id || index"
+                        class="list-card"
+                    >
+                        <!-- 卡片头部 -->
+                        <view class="card-header">
+                            <view class="student-info">
+                                <text class="student-name">{{ item.studentName || '-' }}</text>
+                                <text class="student-no">{{ item.studentNo || '-' }}</text>
+                            </view>
+                            <u-tag
+                                :text="item.leaveTypeName || '-'"
+                                type="primary"
+                                size="mini"
+                            ></u-tag>
+                        </view>
+
+                        <!-- 卡片内容 -->
+                        <view class="card-content">
+                            <view class="info-row">
+                                <text class="label">学院:</text>
+                                <text class="value">{{ item.collegeName || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">专业:</text>
+                                <text class="value">{{ item.majorName || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">班级:</text>
+                                <text class="value">{{ item.className || '-' }}</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">请假时间:</text>
+                                <text class="value">
+                                    {{ item.startTime }} 至 {{ item.endTime }}
+                                </text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">请假时长:</text>
+                                <text class="value">{{ item.leaveDuration || 0 }}天</text>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">是否离校:</text>
+                                <u-tag
+                                    :text="getIsLeaveSchoolText(item.isLeaveSchool)"
+                                    :type="getIsLeaveSchoolTagType(item.isLeaveSchool)"
+                                    size="mini"
+                                ></u-tag>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">审批状态:</text>
+                                <u-tag
+                                    :text="getApprovalStatusText(item.approvalStatus)"
+                                    :type="getApprovalStatusTagType(item.approvalStatus)"
+                                    size="mini"
+                                ></u-tag>
+                            </view>
+                            <view class="info-row">
+                                <text class="label">销假状态:</text>
+                                <u-tag
+                                    :text="getCancelStatusText(item.cancelStatus)"
+                                    :type="getCancelStatusTagType(item.cancelStatus)"
+                                    size="mini"
+                                ></u-tag>
+                            </view>
+                        </view>
+
+                        <!-- 操作按钮 -->
+                        <view class="card-actions">
+                            <u-button
+                                type="primary"
+                                size="small"
+                                plain
+                                @click="handleViewApproval(item)"
+                            >
+                                <u-icon
+                                    name="list"
+                                    size="16"
+                                    color="#fff"
+                                    style="margin-right: 4px"
+                                ></u-icon>
+                                审批记录
+                            </u-button>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 加载更多提示 -->
+                <view class="load-more-wrapper">
+                    <u-loadmore :status="loadMoreStatus" :load-text="loadMoreText" />
+                </view>
+            </scroll-view>
+        </view>
+
+        <!-- 审批记录组件 -->
+        <approval-record ref="approvalRecordRef"></approval-record>
+    </view>
+</template>
+
+<script>
+import { request } from '@/common/request.js';
+import { getDict, getDictLabel, getDictTagType } from '@/common/dict.js';
+import ApprovalRecord from '@/components/Process/approvalRecord.vue';
+
+export default {
+    name: 'StudentLeaveTab',
+    components: {
+        ApprovalRecord,
+    },
+    data() {
+        return {
+            loading: false,
+            dataList: [],
+            total: 0,
+            searchKeyword: '', // 搜索关键字(姓名或学号)
+
+            // 查询参数
+            queryParams: {
+                pageNum: 1,
+                pageSize: 10,
+                studentName: '',
+                studentNo: '',
+            },
+
+            // 加载更多状态
+            loadMoreStatus: 'loadmore',
+            loadMoreText: {
+                loadmore: '点击加载更多',
+                loading: '正在加载...',
+                nomore: '没有更多了',
+            },
+
+            // 字典数据
+            dictYesNo: [],
+            dictApprovalStatus: [],
+            dictCancelStatus: [],
+        };
+    },
+
+    async created() {
+        await this.loadDictData();
+        await this.getList();
+    },
+
+    methods: {
+        // 加载字典数据
+        async loadDictData() {
+            try {
+                this.dictYesNo = await getDict('sys_yesno');
+                this.dictApprovalStatus = await getDict('student_leave_approval_status');
+                this.dictCancelStatus = await getDict('xg_xsqj_cancel_status');
+            } catch (error) {
+                console.error('加载字典数据失败:', error);
+            }
+        },
+
+        // 获取列表数据
+        async getList() {
+            this.loading = true;
+            try {
+                // 根据搜索关键字设置查询参数
+                const params = {
+                    ...this.queryParams,
+                    studentName: this.searchKeyword,
+                    studentNo: this.searchKeyword,
+                };
+
+                const res = await request({
+                    url: '/studentDynamics/studentLeaveList',
+                    method: 'GET',
+                    params: params,
+                });
+
+                if (res && res.code === 200) {
+                    if (this.queryParams.pageNum === 1) {
+                        this.dataList = res.rows || [];
+                    } else {
+                        this.dataList = [...this.dataList, ...(res.rows || [])];
+                    }
+                    this.total = res.total || 0;
+
+                    // 更新加载更多状态
+                    if (this.dataList.length >= this.total) {
+                        this.loadMoreStatus = 'nomore';
+                    } else {
+                        this.loadMoreStatus = 'loadmore';
+                    }
+                } else {
+                    uni.showToast({
+                        title: res?.msg || '获取数据失败',
+                        icon: 'none',
+                    });
+                }
+            } catch (error) {
+                console.error('获取学生请假信息列表失败:', error);
+                uni.showToast({
+                    title: '获取数据失败',
+                    icon: 'none',
+                });
+            } finally {
+                this.loading = false;
+            }
+        },
+
+        // 加载更多
+        handleLoadMore() {
+            if (this.loadMoreStatus === 'nomore' || this.loadMoreStatus === 'loading') {
+                return;
+            }
+
+            this.loadMoreStatus = 'loading';
+            this.queryParams.pageNum++;
+            this.getList();
+        },
+
+        // 搜索操作
+        handleQuery() {
+            this.queryParams.pageNum = 1;
+            this.dataList = [];
+            this.getList();
+        },
+
+        // 清空搜索
+        handleClear() {
+            this.searchKeyword = '';
+            this.handleQuery();
+        },
+
+        // 获取是否离校文本
+        getIsLeaveSchoolText(value) {
+            return getDictLabel(this.dictYesNo, value) || '-';
+        },
+
+        // 获取是否离校标签类型
+        getIsLeaveSchoolTagType(value) {
+            const fallbackMap = {
+                Y: 'warning', // 是
+                N: 'info', // 否
+            };
+            return getDictTagType(this.dictYesNo, value, fallbackMap);
+        },
+
+        // 获取审批状态文本
+        getApprovalStatusText(value) {
+            return getDictLabel(this.dictApprovalStatus, value) || '-';
+        },
+
+        // 获取审批状态标签类型
+        getApprovalStatusTagType(value) {
+            const fallbackMap = {
+                0: 'warning', // 待审批
+                1: 'success', // 已通过
+                2: 'error', // 已驳回
+            };
+            return getDictTagType(this.dictApprovalStatus, value, fallbackMap);
+        },
+
+        // 获取销假状态文本
+        getCancelStatusText(value) {
+            return getDictLabel(this.dictCancelStatus, value) || '-';
+        },
+
+        // 获取销假状态标签类型
+        getCancelStatusTagType(value) {
+            const fallbackMap = {
+                0: 'warning', // 未销假
+                1: 'success', // 已销假
+            };
+            return getDictTagType(this.dictCancelStatus, value, fallbackMap);
+        },
+
+        // 查看审批记录
+        handleViewApproval(item) {
+            console.log('查看审批记录:', item);
+            if (!item.id) {
+                uni.showToast({
+                    title: '数据异常,无法查看审批记录',
+                    icon: 'none',
+                });
+                return;
+            }
+            // 调用审批记录组件,传入业务ID
+            this.$refs.approvalRecordRef.init(item.id);
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.student-leave-tab {
+    background-color: #f5f6fa;
+    min-height: calc(100vh - 176rpx);
+}
+
+.search-container {
+    padding: 20rpx;
+    background-color: #fff;
+    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
+}
+
+.content-wrapper {
+    padding: 20rpx;
+}
+
+.empty-container {
+    padding: 100rpx 20rpx;
+}
+
+.scroll-view {
+    height: calc(100vh - 176rpx - 88rpx); // 减去搜索栏高度
+}
+
+.list-container {
+    display: flex;
+    flex-direction: column;
+    gap: 20rpx;
+    padding-bottom: 20rpx;
+}
+
+.list-card {
+    background-color: #fff;
+    border-radius: 16rpx;
+    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
+    overflow: hidden;
+}
+
+.card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 24rpx;
+    border-bottom: 1px solid #f0f0f0;
+}
+
+.student-info {
+    display: flex;
+    align-items: center;
+    gap: 16rpx;
+}
+
+.student-name {
+    font-size: 32rpx;
+    font-weight: bold;
+    color: #303133;
+}
+
+.student-no {
+    font-size: 26rpx;
+    color: #909399;
+}
+
+.card-content {
+    padding: 24rpx;
+}
+
+.info-row {
+    display: flex;
+    align-items: center;
+    margin-bottom: 16rpx;
+
+    &:last-child {
+        margin-bottom: 0;
+    }
+}
+
+.label {
+    font-size: 28rpx;
+    color: #606266;
+    min-width: 140rpx;
+    margin-right: 16rpx;
+}
+
+.value {
+    font-size: 28rpx;
+    color: #303133;
+    flex: 1;
+    word-break: break-all;
+}
+
+.card-actions {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    padding: 20rpx 24rpx;
+    border-top: 1px solid #f0f0f0;
+}
+
+.load-more-wrapper {
+    padding: 20rpx 0;
+}
+
+// uview组件样式调整
+::v-deep .u-search {
+    background-color: #f5f6fa;
+}
+</style>

+ 99 - 0
mobile/src/pages/xg/studentDynamics/index.vue

@@ -0,0 +1,99 @@
+<template>
+    <view class="student-dynamics-container">
+        <!-- 顶部导航 -->
+        <u-navbar title="学生动向信息" :border-bottom="false" bg-color="#0051d9" title-color="#fff">
+            <view slot="left" @click="goBack">
+                <u-icon name="arrow-left" color="#fff" size="22"></u-icon>
+            </view>
+        </u-navbar>
+
+        <!-- Tab切换 -->
+        <u-tabs
+            :list="tabList"
+            :current="currentTab"
+            @change="handleTabChange"
+            active-color="#0051d9"
+            inactive-color="#606266"
+            line-color="#0051d9"
+            :line-width="60"
+            :line-height="4"
+            :scrollable="false"
+        ></u-tabs>
+
+        <!-- Tab内容区域 -->
+        <view class="tab-content">
+            <!-- 调宿/退宿信息 -->
+            <view v-show="currentTab === 0" class="tab-pane">
+                <dorm-application-tab ref="dormApplicationTabRef"></dorm-application-tab>
+            </view>
+
+            <!-- 假期留宿信息 -->
+            <view v-show="currentTab === 1" class="tab-pane">
+                <holiday-stay-tab ref="holidayStayTabRef"></holiday-stay-tab>
+            </view>
+
+            <!-- 学生请假信息 -->
+            <view v-show="currentTab === 2" class="tab-pane">
+                <student-leave-tab ref="studentLeaveTabRef"></student-leave-tab>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+import DormApplicationTab from './components/DormApplicationTab.vue';
+import HolidayStayTab from './components/HolidayStayTab.vue';
+import StudentLeaveTab from './components/StudentLeaveTab.vue';
+
+export default {
+    name: 'StudentDynamics',
+    components: {
+        DormApplicationTab,
+        HolidayStayTab,
+        StudentLeaveTab,
+    },
+    data() {
+        return {
+            // Tab列表
+            tabList: [
+                { name: '调宿/退宿信息' },
+                { name: '假期留宿信息' },
+                { name: '学生请假信息' },
+            ],
+            // 当前激活的Tab
+            currentTab: 0,
+        };
+    },
+    onLoad() {
+        console.log('学生动向信息管理页面已加载');
+    },
+    methods: {
+        // 返回上一页
+        goBack() {
+            uni.navigateBack();
+        },
+
+        // Tab切换处理
+        handleTabChange(item) {
+            console.log('切换到Tab页:', item);
+            // uview的@change事件返回的是对象,需要提取index属性
+            this.currentTab = item.index;
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.student-dynamics-container {
+    background-color: #f5f6fa;
+    min-height: 100vh;
+}
+
+.tab-content {
+    padding: 0;
+}
+
+.tab-pane {
+    min-height: calc(100vh - 88rpx - 88rpx); // 减去navbar和tabs的高度
+}
+</style>

+ 1 - 1
mobile/src/pages/xg/xsjcxx/xsws/stuMsgDetailDialog.vue

@@ -17,7 +17,7 @@
                 :list="tabList"
                 :current="safeCurrentTab"
                 @click="handleTabClick"
-                active-color="#3c9cff"
+                active-color="#0051d9"
                 inactive-color="#606266"
                 :font-size="32"
                 :bar-width="60"