| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
- <title>班主任点名</title>
- <!-- 引入基础依赖 -->
- <script src="/js/mp_base/base.js"></script>
- <style>
- /* 防止Vue模板闪烁 */
- [v-cloak] {
- display: none !important;
- }
- #app {
- background: #f5f5f5;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- }
- /* 固定头部区域 */
- .fixed-header {
- background: #f5f5f5;
- padding: 10px 0;
- position: sticky;
- top: 0;
- z-index: 100;
- }
-
- .filter-area.simple-mode {
- /* padding: 10px 16px; */
- /* background: #f8f9fa; */
- /* border-radius: 6px; */
- transition: all 0.3s ease;
- }
- .simple-display {
- display: flex;
- align-items: center;
- justify-content: center;
- height: 40px;
- }
- .simple-text {
- font-size: 16px;
- color: #333;
- }
- /* 统计区域 */
- .stats-area {
- display: flex;
- justify-content: flex-start;
- padding: 10px 16px;
- background: #ffffff;
- border-radius: 6px;
- gap: 8px;
- }
- .stat-item {
- text-align: center;
- font-size: 14px;
- }
- .stat-absent { color: #ff0000; }
- .stat-sick { color: #ee9700; }
- .stat-parent { color: #00a0e9; }
- /* 学生列表区域 */
- .student-list-area {
- flex: 1;
- /* padding: 8px; */
- padding-bottom: 80px; /* 给底部按钮留出空间 */
- overflow-y: auto;
- width: calc(100% - 16px);
- margin: 8px auto;
- }
- /* 学生表格 */
- .student-table {
- background: white;
- overflow: hidden;
- border: 1px solid #d2d2d2;
- }
- .table-header {
- display: flex;
- background: #f8f9fa;
- font-weight: 400;
- border-bottom: 1px solid #d2d2d2;
- }
- .table-row {
- display: flex;
- border-bottom: 1px solid #d2d2d2;
- cursor: pointer;
- transition: background-color 0.2s;
- }
- .table-row:last-child {
- border-bottom: none;
- }
-
- .table-row.absent {
- background: #ff0000;
- color: white !important;
- }
- .table-row.absent .col-number,
- .table-row.absent .col-name,
- .table-row.absent .col-status {
- color: white !important;
- }
- .table-row.sick {
- background: #ffa502;
- color: white !important;
- }
- .table-row.sick .col-number,
- .table-row.sick .col-name,
- .table-row.sick .col-status {
- color: white !important;
- }
- .table-row.leave {
- background: #ee9700;
- color: white !important;
- }
- .table-row.leave .col-number,
- .table-row.leave .col-name,
- .table-row.leave .col-status {
- color: white !important;
- }
- .table-row.parent {
- background: #00a0e9;
- color: white !important;
- }
- .table-row.parent .col-number,
- .table-row.parent .col-name,
- .table-row.parent .col-status {
- color: white !important;
- }
- .col-number {
- width: 60px;
- padding: 12px 8px;
- text-align: center;
- font-size: 16px;
- color: #333;
- }
- .col-name {
- flex: 1;
- padding: 12px;
- font-size: 16px;
- color: #333;
- }
- .col-status {
- width: 80px;
- padding: 12px 8px;
- text-align: center;
- font-size: 16px;
- font-weight: 400;
- color: #333;
- }
- /* 加载状态 */
- .loading {
- text-align: center;
- padding: 40px;
- color: #666;
- }
- /* 周几显示 */
- .weekday-text {
- margin-left: 10px;
- font-size: 12px;
- color: #666;
- position:absolute;
- right: 16px;
- }
- </style>
- </head>
- <body>
- <div id="app" v-cloak>
- <!-- 固定头部区域 -->
- <div class="fixed-header">
- <!-- 筛选条件区域 -->
- <div class="filter-area" :class="{ 'simple-mode': isSimpleMode }">
- <!-- 简化模式:显示回显文本 -->
- <div v-if="isSimpleMode" class="simple-display">
- <span class="simple-text">{{ displayText }}</span>
- </div>
- <!-- 完整模式:显示表单 -->
- <div v-if="!isSimpleMode" class="full-form">
- <table class="form-table">
- <tr>
- <th>班级</th>
- <td>
- <ss-select
- v-model="formData.bjid"
- :options="bjOptions"
- :mapping="{ text: 'label', value: 'value' }"
- placeholder="请选择班级"
- @change="loadStudentList"
- >
- </ss-select>
- </td>
- </tr>
- <tr>
- <th>日期</th>
- <td>
- <ss-datetime-picker
- v-model="formData.rq"
- mode="date"
- :max-date="new Date().toISOString().slice(0,10)"
- placeholder="请选择日期"
- @change="onDateChange"
- :disabled="true"
- >
- </ss-datetime-picker>
- <span v-if="weekdayText" class="weekday-text">{{ weekdayText }}</span>
- </td>
- </tr>
- <tr>
- <th>节次</th>
- <td>
- <ss-onoff-button v-model="formData.jc" name="jc" label="上午" value="1" :disabled="true"></ss-onoff-button>
- <ss-onoff-button v-model="formData.jc" name="jc" label="下午" value="2" :disabled="true"></ss-onoff-button>
- <ss-onoff-button v-model="formData.jc" name="jc" label="晚上" value="3" :disabled="true"></ss-onoff-button>
- </td>
- </tr>
- </table>
- </div>
- </div>
- </div>
- <!-- 考勤统计 -->
- <div class="stats-area">
- <div class="stat-item">
- <span class="stat-absent">缺勤:</span>{{ stats.absent }}人
- </div>
- <div class="stat-item">
- <span class="stat-sick">请假:</span>{{ stats.sick + stats.leave }}人
- </div>
- <div class="stat-item">
- <span class="stat-parent">家长请假:</span>{{ stats.parentLeave }}人
- </div>
- </div>
- <!-- 学生列表区域 -->
- <div class="student-list-area">
- <div v-if="loading" class="loading">
- 加载中...
- </div>
- <div v-else-if="studentList.length === 0" class="loading">
- 暂无学生数据
- </div>
- <div v-else class="student-table">
- <!-- 表头 -->
- <div class="table-header">
- <span class="col-number">序号</span>
- <span class="col-name">姓名</span>
- <span class="col-status">状态</span>
- </div>
- <!-- 学生行 -->
- <div
- v-for="(student, index) in studentList"
- :key="student.id"
- class="table-row"
- :class="getRowClass(student.kqlbm, student.rcid)"
- @click="handleStudentClick(student)"
- >
- <span class="col-number">{{ String(index + 1).padStart(2, '0') }}</span>
- <span class="col-name">{{ student.xm }}</span>
- <span class="col-status">{{ getStatusText(student.kqlbm, student.rcid) }}</span>
- </div>
- </div>
- </div>
- <!-- 使用SsBottom组件 -->
- <ss-bottom
- :show-shyj="false"
- :buttons="bottomButtons"
- @button-click="handleBottomAction"
- ></ss-bottom>
- <!-- 确认弹窗组件 -->
- <ss-confirm v-model="showConfirm" title="确认提交" @confirm="submitAttendance">
- <div style="text-align: center; padding: 20px;">
-
- <div style="display: flex; flex-direction: column; gap: 10px;">
- <div>出勤:<span >{{ stats.present }}人</span></div>
- <div>缺勤:<span >{{ stats.absent }}人</span></div>
- <div>病假:<span >{{ stats.sick }}人</span></div>
- <div>事假:<span >{{ stats.leave }}人</span></div>
- <div>家长请假:<span >{{ stats.parentLeave }}人</span></div>
- </div>
- </div>
- </ss-confirm>
- </div>
- <script>
- // 等待SS框架加载完成
- window.SS.ready(function () {
- // 使用SS框架的方式创建Vue实例
- window.SS.dom.initializeFormApp({
- el: '#app',
- data() {
- return {
- // 页面参数
- pageParams: {},
- // 加载状态
- loading: true,
- // 表单数据
- formData: {
- bjid: '', // 班级ID
- rq: '', // 日期
- jc: '1' // 节次:1-上午,2-下午,3-晚上
- },
- // 显示模式
- isSimpleMode: false,
- displayText: '',
- weekdayText: '',
- // 滚动相关
- pageScrollTop: 0,
- initialFilterHeight: 0,
- // 统计数据
- stats: {
- absent: 0, // 缺勤
- leave: 0, // 请假
- parent: 0 // 家长请假
- },
- // 学生列表
- studentList: [],
- // 班级选项
- bjOptions: [],
-
- // 确认弹窗相关
- showConfirm: false,
- confirmStatsHtml: '',
- // 底部按钮
- bottomButtons: [
- { text: '取消', action: 'cancel' },
- { text: '保存点名', action: 'save' }
- ]
- }
- },
- async mounted() {
- try {
- // 获取页面参数
- this.pageParams = NavigationManager.getUrlParam();
- console.log('📋 页面参数:', this.pageParams);
- // 设置页面标题
- if (this.pageParams.title) {
- document.title = this.pageParams.title;
- }
- // 初始化页面
- await this.initPage();
- } catch (error) {
- console.error('❌ 页面初始化失败:', error);
- }
- },
- methods: {
- // 初始化页面
- async initPage() {
- try {
- // 设置默认日期为今天
- this.formData.rq = new Date().toISOString().slice(0, 10);
- this.updateWeekdayText();
- // 根据当前时间设置默认节次
- this.setDefaultPeriod();
- // 初始化回显文本
- this.updateDisplayText();
- // 初始化筛选区域高度
- this.$nextTick(() => {
- const filterArea = document.querySelector('.filter-area');
- if (filterArea) {
- this.initialFilterHeight = filterArea.offsetHeight;
- console.log('初始筛选区域高度:', this.initialFilterHeight);
- }
- });
- // 添加滚动监听
- window.addEventListener('scroll', this.handlePageScroll);
- // 加载班级选项
- await this.loadBjOptions();
- // 如果有默认班级,加载学生数据
- if (this.formData.bjid) {
- await this.loadStudentList();
- }
- } catch (error) {
- console.error('❌ 页面初始化失败:', error);
- throw error;
- } finally {
- this.loading = false;
- }
- },
- // 获取开始时间
- getKssj() {
- const jc = this.formData.jc;
- const rq = this.formData.rq;
- let time = '';
- if (jc === '1') {
- time = '08:00:00'; // 上午
- } else if (jc === '2') {
- time = '14:00:00'; // 下午
- } else if (jc === '3') {
- time = '19:00:00'; // 晚上
- }
- return `${rq} ${time}`;
- },
- // 获取结束时间
- getJssj() {
- const jc = this.formData.jc;
- const rq = this.formData.rq;
- let time = '';
- if (jc === '1') {
- time = '12:00:00'; // 上午
- } else if (jc === '2') {
- time = '18:00:00'; // 下午
- } else if (jc === '3') {
- time = '22:00:00'; // 晚上
- }
- return `${rq} ${time}`;
- },
- // 页面销毁时移除滚动监听
- beforeUnmount() {
- window.removeEventListener('scroll', this.handlePageScroll);
- },
- // 组件初始化逻辑已改为模板中直接使用 Vue 组件,无需此方法
- initFormComponents() {},
- // 日期变化处理
- onDateChange(value) {
- this.formData.rq = value;
- this.updateWeekdayText();
- this.updateDisplayText();
- this.loadStudentList();
- },
- // 更新星期几显示
- updateWeekdayText() {
- if (!this.formData.rq) {
- this.weekdayText = '';
- return;
- }
- try {
- const date = new Date(this.formData.rq);
- const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
- this.weekdayText = weekdays[date.getDay()];
- // 更新回显文本
- this.updateDisplayText();
- } catch (error) {
- this.weekdayText = '';
- }
- },
- // 根据当前时间设置默认节次
- setDefaultPeriod() {
- const currentTime = new Date();
- const currentHour = currentTime.getHours();
- let currentPeriod = '';
- let jcValue = '1';
- if (currentHour >= 0 && currentHour < 12) {
- // 0-12点:上午
- currentPeriod = '上午';
- jcValue = '1';
- } else if (currentHour >= 12 && currentHour < 18) {
- // 12-18点:下午
- currentPeriod = '下午';
- jcValue = '2';
- } else {
- // 18-24点:晚上
- currentPeriod = '晚上';
- jcValue = '3';
- }
- this.formData.jc = jcValue;
- console.log(`🕐 当前时间 ${currentHour}:xx,自动设置为${currentPeriod}(${jcValue})`);
- },
- // 计算回显文本
- updateDisplayText() {
- const jcText = this.formData.jc === '1' ? '上午' : this.formData.jc === '2' ? '下午' : '晚上';
- this.displayText = `${this.formData.rq} ${this.weekdayText} ${jcText}`;
- },
- // 处理页面滚动事件
- handlePageScroll() {
- const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
- const previousScrollTop = this.pageScrollTop;
- this.pageScrollTop = scrollTop;
- console.log('页面滚动位置:', scrollTop, '初始高度:', this.initialFilterHeight);
- // 检测滚动方向和位置
- const isScrollingDown = scrollTop > previousScrollTop;
- const isScrollingUp = scrollTop < previousScrollTop;
- // 三行变一行:向下滚动超过筛选区域高度的30px时触发
- if (isScrollingDown &&
- scrollTop > (this.initialFilterHeight + 30) &&
- !this.isSimpleMode) {
- console.log('向下滚动超过阈值,切换到简化模式');
- this.isSimpleMode = true;
- }
- // 一行变三行:向上滚动回到接近顶部时触发
- if (isScrollingUp &&
- scrollTop <= 10 &&
- this.isSimpleMode) {
- console.log('向上滚动回到顶部,切换到完整模式');
- this.isSimpleMode = false;
- }
- },
- // 加载班级选项
- async loadBjOptions() {
- try {
- // 使用已封装的 getDictOptions 获取班级数据
- const bjOptions = await window.getDictOptions('bj');
- if (bjOptions && bjOptions.length > 0) {
- this.bjOptions = bjOptions.map(item => ({
- value: item.v,
- label: item.n
- }));
- console.log('📚 班级选项加载成功:', this.bjOptions);
- // 设置默认选中第一个班级
- if (this.bjOptions.length > 0) {
- this.formData.bjid = this.bjOptions[0].value;
- }
- } else {
- console.warn('⚠️ 没有获取到班级数据');
- }
- } catch (error) {
- console.error('❌ 加载班级选项失败:', error);
- }
- },
- // 加载学生列表
- async loadStudentList() {
- if (!this.formData.bjid) {
- console.warn('⚠️ 班级ID为空,无法加载学生列表');
- return;
- }
- try {
- this.loading = true;
- console.log('👥 加载学生列表...', { bjid: this.formData.bjid });
- // 调用真实的 bjdm_initBzrDm 接口
- const response = await window.request.post(
- `/service?ssServ=bjdm_initBzrdm`,
- { bjid: this.formData.bjid },
- {
- loading: true,
- formData: true
- }
- );
- console.log('👥 学生数据返回:', response);
- if (response.data && response.data.bjList && response.data.bjList.length > 0) {
- // 取第一个班级的学生列表
- this.studentList = response.data.bjList[0];
- this.updateStats();
- } else {
- console.log('⚠️ 没有获取到学生数据:', response.data?.msg);
- this.studentList = [];
- }
- } catch (error) {
- console.log('❌ 加载学生列表失败:', error);
-
- // this.updateStats();
- } finally {
- this.loading = false;
- }
- },
- // 更新统计数据
- updateStats() {
- const stats = {
- present: 0, // 出勤
- absent: 0, // 缺勤
- sick: 0, // 病假
- leave: 0, // 事假
- parentLeave: 0 // 家长请假
- };
- this.studentList.forEach(student => {
- if (student.rcid > 0) {
- stats.parentLeave++ // 家长请假(优先判断)
- } else if (student.kqlbm === 81) {
- stats.present++ // 出勤
- } else if (student.kqlbm === 1) {
- stats.absent++ // 缺勤
- } else if (student.kqlbm === 31) {
- stats.leave++ // 事假
- } else if (student.kqlbm === 41) {
- stats.sick++ // 病假
- } else {
- stats.present++ // 其他情况默认为出勤(学生一开始都是出勤状态)
- }
- });
- this.stats = stats;
- },
- // 获取行样式类
- getRowClass(kqlbm, rcid) {
- if (rcid > 0) return 'parent'; // 家长请假(优先判断)
- if (kqlbm === 81) return ''; // 出勤(默认样式)
- if (kqlbm === 1) return 'absent'; // 缺勤
- if (kqlbm === 41) return 'sick'; // 病假
- if (kqlbm === 31) return 'leave'; // 事假
- return '';
- },
- // 获取状态文本
- getStatusText(kqlbm, rcid) {
- if (rcid > 0) return '家长请假'; // 家长请假(优先判断)
- if (kqlbm === 81) return '出勤';
- if (kqlbm === 1) return '缺勤';
- if (kqlbm === 41) return '病假';
- if (kqlbm === 31) return '事假';
- return '出勤'; // 默认为出勤,不存在未知状态
- },
- // 学生点击事件
- handleStudentClick(student) {
- console.log('👤 学生点击:', student);
- // 家长请假不可点击
- if (student.rcid > 0) {
- console.log('家长请假状态,不可点击');
- return;
- }
- // 切换学生状态
- this.toggleStudentStatus(student);
- },
- // 切换学生状态
- toggleStudentStatus(student) {
- /**
- * 状态切换逻辑:出勤(81) → 缺勤(1) → 病假(41) → 事假(31) → 出勤(81)
- * 只在这四个状态间轮流切换,不存在其他状态
- */
- let newStatus;
- switch (student.kqlbm) {
- case 81: // 出勤 → 缺勤
- newStatus = 1;
- break;
- case 1: // 缺勤 → 病假
- newStatus = 41;
- break;
- case 41: // 病假 → 事假
- newStatus = 31;
- break;
- case 31: // 事假 → 出勤
- newStatus = 81;
- break;
- default: // 任何其他状态都重置为出勤
- newStatus = 81;
- }
- student.kqlbm = newStatus;
- student.rcid = null; // 清除家长请假标记
- console.log(`学生 ${student.xm} 状态切换为:`, newStatus);
- this.updateStats();
- },
- // 保存点名数据
- async handleSave() {
- try {
- // 显示确认弹窗
- this.showConfirmDialog();
- } catch (error) {
- console.error('❌ 保存失败:', error);
- }
- },
- // 显示确认弹窗
- showConfirmDialog() {
- this.showConfirm = true;
- },
- // 提交考勤数据
- async submitAttendance() {
- try {
- // 构建提交参数,参考小程序版本
- const params = {
- bjid: this.formData.bjid,
- kkrs: this.stats.absent, // 缺勤人数
- qjrs: this.stats.sick + this.stats.leave, // 请假人数(病假+事假)
- jkssj: this.getKssj(), // 开始时间
- jjssj: this.getJssj(), // 结束时间
- ryList: JSON.stringify(this.studentList
- .filter(student => student.kqlbm !== 81) // 只提交非出勤的学生
- .map(student => ({
- ryid: student.ryid,
- kqlbm: student.kqlbm
- })))
- };
- // 调用真实的 bjdm_saveBzrDm 接口,使用表单格式提交
- const response = await window.request.post(`/service?ssServ=bjdm_saveBzrdm`, params, {
- loading: true,
- formData: true // 使用表单格式提交
- });
- if (response.data && response.data.ssCode === 0) {
- NavigationManager.goBack({ refreshParent: true });
- }
- } catch (error) {
- console.error('❌ 提交失败:', error);
- }
- },
- // 获取开始时间
- getKssj() {
- const jc = this.formData.jc;
- const rq = this.formData.rq;
- let time = '';
- if (jc === '1') {
- time = '08:00:00'; // 上午
- } else if (jc === '2') {
- time = '14:00:00'; // 下午
- } else if (jc === '3') {
- time = '19:00:00'; // 晚上
- }
- return `${rq} ${time}`;
- },
- // 获取结束时间
- getJssj() {
- const jc = this.formData.jc;
- const rq = this.formData.rq;
- let time = '';
- if (jc === '1') {
- time = '12:00:00'; // 上午
- } else if (jc === '2') {
- time = '18:00:00'; // 下午
- } else if (jc === '3') {
- time = '22:00:00'; // 晚上
- }
- return `${rq} ${time}`;
- },
- // 处理底部按钮点击
- handleBottomAction(data) {
- console.log('底部按钮操作:', data);
- switch(data.action) {
- case 'cancel':
- this.handleCancel();
- break;
- case 'save':
- this.handleSave();
- break;
- default:
- console.warn('未知的按钮操作:', data.action);
- }
- },
- // 取消返回
- handleCancel() {
- NavigationManager.goBack({ refreshParent: false });
- },
-
- }
- })
- })
- </script>
- </body>
- </html>
|