Browse Source

修改 bug

master
王泽彦 7 months ago
parent
commit
e3b3237721
  1. BIN
      doc/副本课程协议—月卡篮球(1).docx
  2. 4
      uniapp/common/config.js
  3. 461
      uniapp/pages-coach/coach/schedule/schedule_table.vue
  4. 4
      uniapp/pages-market/clue/add_clues.vue
  5. 12
      uniapp/pages/common/home/index.vue

BIN
doc/副本课程协议—月卡篮球(1).docx

Binary file not shown.

4
uniapp/common/config.js

@ -1,6 +1,6 @@
// 环境变量配置 // 环境变量配置
const env = 'development' // const env = 'development'
// const env = 'prod' const env = 'prod'
const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退 const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退
const isDebug = false // 默认启用调试模式 const isDebug = false // 默认启用调试模式
const devurl = 'http://localhost:20080/api' const devurl = 'http://localhost:20080/api'

461
uniapp/pages-coach/coach/schedule/schedule_table.vue

@ -56,97 +56,31 @@
<!-- 课程表主体 --> <!-- 课程表主体 -->
<view class="schedule-main"> <view class="schedule-main">
<!-- 左侧冻结列 --> <!-- 统一滚动区域 -->
<view class="frozen-column">
<!-- 左上角标题 -->
<view class="time-header-cell">
<template v-if="activeFilter === 'time' || activeFilter === ''">时间</template>
<template v-else-if="activeFilter === 'teacher'">教练</template>
<template v-else-if="activeFilter === 'classroom'">教室</template>
<template v-else-if="activeFilter === 'class'">班级</template>
</view>
<!-- 左侧冻结内容 -->
<scroll-view
class="frozen-content-scroll"
scroll-y
:scroll-top="scrollTop"
:enable-flex="true"
:scroll-anchoring="false"
:enhanced="true"
:bounces="false"
:scroll-with-animation="false"
>
<view class="frozen-content">
<!-- 时间模式 -->
<template v-if="activeFilter === 'time' || activeFilter === ''">
<view
:class="['frozen-cell', !timeSlot.available ? 'time-unavailable' : '']"
v-for="(timeSlot, timeIndex) in timeSlots"
:key="timeIndex"
:ref="`frozenCell_${timeIndex}`"
>
{{ timeSlot.time }}
</view>
</template>
<!-- 教练模式 -->
<template v-else-if="activeFilter === 'teacher'">
<view
class="frozen-cell"
v-for="(teacher, index) in teacherOptions"
:key="teacher.id"
:ref="`frozenCell_${index}`"
>
{{ teacher.name }}
</view>
</template>
<!-- 教室模式 -->
<template v-else-if="activeFilter === 'classroom'">
<view
class="frozen-cell"
v-for="(venue, index) in venues"
:key="venue.id"
:ref="`frozenCell_${index}`"
>
{{ venue.name }}
</view>
</template>
<!-- 班级模式 -->
<template v-else-if="activeFilter === 'class'">
<view
class="frozen-cell"
v-for="(cls, index) in classOptions"
:key="cls.id"
:ref="`frozenCell_${index}`"
>
{{ cls.name }}
</view>
</template>
</view>
</scroll-view>
</view>
<!-- 右侧内容区域 -->
<view class="schedule-content-area">
<!-- 合并的滚动区域表头+内容 -->
<scroll-view <scroll-view
class="schedule-scroll" class="schedule-scroll"
scroll-x scroll-x
scroll-y scroll-y
:scroll-top="scrollTop" :style="{ height: scrollViewHeight + 'px' }"
:enable-flex="true" :enable-flex="true"
:enhanced="isH5"
:scroll-anchoring="false" :scroll-anchoring="false"
:enhanced="true"
:bounces="false" :bounces="false"
:scroll-with-animation="false" :scroll-with-animation="false"
:show-scrollbar="false"
@scroll="onScroll" @scroll="onScroll"
> >
<view class="schedule-container-inner" :style="{ width: tableWidth + 'rpx', minWidth: '1260rpx' }"> <view class="schedule-container-inner" :style="{ width: (tableWidth + 120) + 'rpx', minWidth: '1380rpx' }">
<!-- 表头 --> <!-- 表头行 -->
<view class="date-header-container"> <view class="schedule-header-row">
<!-- 左上角标题 -->
<view class="time-header-cell">
<template v-if="activeFilter === 'time' || activeFilter === ''">时间</template>
<template v-else-if="activeFilter === 'teacher'">教练</template>
<template v-else-if="activeFilter === 'classroom'">教室</template>
<template v-else-if="activeFilter === 'class'">班级</template>
</view>
<!-- 日期列 --> <!-- 日期列 -->
<view <view
class="date-header-cell" class="date-header-cell"
@ -169,6 +103,12 @@
:key="timeIndex" :key="timeIndex"
:ref="`scheduleRow_${timeIndex}`" :ref="`scheduleRow_${timeIndex}`"
> >
<!-- 左侧时间列 -->
<view :class="['left-column-cell', !timeSlot.available ? 'time-unavailable' : '']">
{{ timeSlot.time }}
</view>
<!-- 课程单元格 -->
<view <view
:class="['course-cell',!timeSlot.available ? 'cell-unavailable' : '']" :class="['course-cell',!timeSlot.available ? 'cell-unavailable' : '']"
v-for="(date, dateIndex) in weekDates" v-for="(date, dateIndex) in weekDates"
@ -234,6 +174,12 @@
:key="teacher.id" :key="teacher.id"
:ref="`scheduleRow_${teacherIndex}`" :ref="`scheduleRow_${teacherIndex}`"
> >
<!-- 左侧教练列 -->
<view class="left-column-cell">
{{ teacher.name }}
</view>
<!-- 课程单元格 -->
<view <view
class="course-cell" class="course-cell"
v-for="(date, dateIndex) in weekDates" v-for="(date, dateIndex) in weekDates"
@ -299,6 +245,12 @@
:key="venue.id" :key="venue.id"
:ref="`scheduleRow_${venueIndex}`" :ref="`scheduleRow_${venueIndex}`"
> >
<!-- 左侧教室列 -->
<view class="left-column-cell">
{{ venue.name }}
</view>
<!-- 课程单元格 -->
<view <view
class="course-cell" class="course-cell"
v-for="(date, dateIndex) in weekDates" v-for="(date, dateIndex) in weekDates"
@ -364,6 +316,12 @@
:key="cls.id" :key="cls.id"
:ref="`scheduleRow_${clsIndex}`" :ref="`scheduleRow_${clsIndex}`"
> >
<!-- 左侧班级列 -->
<view class="left-column-cell">
{{ cls.name }}
</view>
<!-- 课程单元格 -->
<view <view
class="course-cell" class="course-cell"
v-for="(date, dateIndex) in weekDates" v-for="(date, dateIndex) in weekDates"
@ -424,7 +382,6 @@
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
</view>
<!-- 筛选弹窗 --> <!-- 筛选弹窗 -->
<fui-modal <fui-modal
@ -554,11 +511,10 @@ export default {
selectedClasses: [], selectedClasses: [],
// //
scrollTop: 0,
scrollTimer: null, // scrollTimer: null, //
// // -
tableWidth: 1500, // 7 (7*180+120=1380rpx) tableWidth: 1260, // 7 (7*180=1260rpx)120rpx
// //
timeSlots: [], timeSlots: [],
@ -591,6 +547,12 @@ export default {
selectedScheduleId: null, selectedScheduleId: null,
showScheduleDetail: false, showScheduleDetail: false,
//
isH5: false,
//
scrollViewHeight: 0,
// //
filterParams: { filterParams: {
start_date: '', start_date: '',
@ -644,12 +606,20 @@ export default {
}, },
mounted() { mounted() {
//
// #ifdef H5
this.isH5 = true
// #endif
this.initCurrentWeek() this.initCurrentWeek()
this.initTimeSlots() this.initTimeSlots()
// //
this.handleResize() this.handleResize()
// scroll-view
this.calculateScrollViewHeight()
// //
this.loadFilterOptions().then(() => { this.loadFilterOptions().then(() => {
this.loadScheduleList() this.loadScheduleList()
@ -679,6 +649,72 @@ export default {
}, },
methods: { methods: {
// - HH:mm
normalizeTime(timeStr) {
if (!timeStr) return '';
//
const cleanTime = String(timeStr).trim();
// HH:mm
if (/^\d{2}:\d{2}$/.test(cleanTime)) {
return cleanTime;
}
// H:mm
if (/^\d{1}:\d{2}$/.test(cleanTime)) {
return '0' + cleanTime;
}
// HH:m
if (/^\d{2}:\d{1}$/.test(cleanTime)) {
return cleanTime.slice(0, 3) + '0' + cleanTime.slice(3);
}
// H:m
if (/^\d{1}:\d{1}$/.test(cleanTime)) {
const [hour, minute] = cleanTime.split(':');
return `0${hour}:0${minute}`;
}
//
try {
const [hour, minute] = cleanTime.split(':');
const h = parseInt(hour).toString().padStart(2, '0');
const m = parseInt(minute || 0).toString().padStart(2, '0');
return `${h}:${m}`;
} catch (e) {
console.warn('时间格式解析失败:', timeStr);
return cleanTime;
}
},
// -
isTimeInSlot(courseTime, slotTime) {
const normalizedCourseTime = this.normalizeTime(courseTime);
const normalizedSlotTime = this.normalizeTime(slotTime);
if (!normalizedCourseTime || !normalizedSlotTime) return false;
//
if (normalizedCourseTime === normalizedSlotTime) return true;
// -
const slotIndex = this.timeSlots.findIndex(slot =>
this.normalizeTime(slot.time) === normalizedSlotTime
);
if (slotIndex >= 0 && slotIndex < this.timeSlots.length - 1) {
const currentSlotTime = this.normalizeTime(this.timeSlots[slotIndex].time);
const nextSlotTime = this.normalizeTime(this.timeSlots[slotIndex + 1].time);
//
return normalizedCourseTime >= currentSlotTime && normalizedCourseTime < nextSlotTime;
}
return false;
},
// //
initCurrentWeek() { initCurrentWeek() {
const today = new Date() const today = new Date()
@ -857,8 +893,8 @@ export default {
getCoursesByTimeAndDate(time, date) { getCoursesByTimeAndDate(time, date) {
const matchedCourses = this.courses.filter(course => { const matchedCourses = this.courses.filter(course => {
if (course.date !== date) return false if (course.date !== date) return false
// // 使
return course.time === time return this.isTimeInSlot(course.time, time)
}) })
return matchedCourses return matchedCourses
}, },
@ -933,8 +969,7 @@ export default {
this.filterParams.venue_id = ''; this.filterParams.venue_id = '';
this.filterParams.class_id = ''; this.filterParams.class_id = '';
// //
this.scrollTop = 0;
// //
this.loadScheduleList(); this.loadScheduleList();
@ -956,9 +991,8 @@ export default {
this.applyFilters() this.applyFilters()
this.closeFilterModal() this.closeFilterModal()
// //
await this.loadScheduleList() await this.loadScheduleList()
this.scrollTop = 0
// //
if (this.selectedTimeRange !== '' || this.selectedVenueId !== null) { if (this.selectedTimeRange !== '' || this.selectedVenueId !== null) {
@ -1146,10 +1180,15 @@ export default {
if (res.code === 1) { if (res.code === 1) {
// //
this.courses = res.data.list.map(item => ({ this.courses = res.data.list.map(item => {
//
const rawTime = item.time_info?.start_time || item.time_slot?.split('-')[0] || '';
const normalizedTime = this.normalizeTime(rawTime);
return {
id: item.id, id: item.id,
date: item.course_date, date: item.course_date,
time: item.time_info?.start_time || item.time_slot?.split('-')[0], time: normalizedTime, // 使
courseName: item.course_name || '未命名课程', courseName: item.course_name || '未命名课程',
students: `已报名${item.enrolled_count || 0}`, students: `已报名${item.enrolled_count || 0}`,
teacher: item.coach_name || '待分配', teacher: item.coach_name || '待分配',
@ -1164,16 +1203,12 @@ export default {
duration: item.time_info?.duration || 60, duration: item.time_info?.duration || 60,
time_slot: item.time_slot, time_slot: item.time_slot,
raw: item, // raw: item, //
})) }
})
// //
this.updateLeftColumnData(); this.updateLeftColumnData();
//
this.$nextTick(() => {
this.syncRowHeights();
});
} else { } else {
uni.showToast({ uni.showToast({
title: res.msg || '加载课程安排列表失败', title: res.msg || '加载课程安排列表失败',
@ -1266,61 +1301,18 @@ export default {
}, },
// // -
syncRowHeights() {
this.$nextTick(() => {
try {
let itemCount = 0;
//
if (this.activeFilter === 'time' || this.activeFilter === '') {
itemCount = this.timeSlots.length;
} else if (this.activeFilter === 'teacher') {
itemCount = this.teacherOptions.length;
} else if (this.activeFilter === 'classroom') {
itemCount = this.venues.length;
} else if (this.activeFilter === 'class') {
itemCount = this.classOptions.length;
}
//
for (let i = 0; i < itemCount; i++) {
const scheduleRow = this.$refs[`scheduleRow_${i}`];
const frozenCell = this.$refs[`frozenCell_${i}`];
if (scheduleRow && scheduleRow[0] && frozenCell && frozenCell[0]) {
//
const rightRowHeight = scheduleRow[0].$el ?
scheduleRow[0].$el.offsetHeight :
scheduleRow[0].offsetHeight;
//
if (frozenCell[0].$el) {
frozenCell[0].$el.style.minHeight = rightRowHeight + 'px';
} else if (frozenCell[0].style) {
frozenCell[0].style.minHeight = rightRowHeight + 'px';
}
}
}
} catch (error) {
console.warn('高度同步失败:', error);
}
});
},
// -
onScroll(e) { onScroll(e) {
// 使 //
//
if (this.scrollTimer) { if (this.scrollTimer) {
clearTimeout(this.scrollTimer) clearTimeout(this.scrollTimer)
} }
this.scrollTimer = setTimeout(() => { this.scrollTimer = setTimeout(() => {
// //
if (e.detail.scrollTop !== undefined && e.detail.scrollTop !== this.scrollTop) { // /
this.scrollTop = e.detail.scrollTop }, 16)
}
}, 16) // 60fps
}, },
// //
@ -1523,6 +1515,36 @@ export default {
// API // API
}, },
// scroll-view
calculateScrollViewHeight() {
uni.getSystemInfo({
success: (res) => {
//
let screenHeight = res.screenHeight || res.windowHeight
//
// + + +
let otherHeight = 0
// #ifdef H5
otherHeight = 200 // H5200px
// #endif
// #ifdef MP-WEIXIN
otherHeight = 160 // 160px (rpx)
// #endif
// scroll-view
this.scrollViewHeight = screenHeight - otherHeight
//
if (this.scrollViewHeight < 300) {
this.scrollViewHeight = 300
}
}
})
},
// //
handleResize() { handleResize() {
// //
@ -1545,12 +1567,15 @@ export default {
// #endif // #endif
if (width <= 375) { if (width <= 375) {
this.tableWidth = 1220 // 7*160+100=1220rpx this.tableWidth = 1120 // 7*160=1120rpx (160rpx)
} else if (width <= 768) { } else if (width <= 768) {
this.tableWidth = 1400 this.tableWidth = 1260 // 7*180=1260rpx
} else { } else {
this.tableWidth = 1500 this.tableWidth = 1260 // 7*180=1260rpx
} }
// scroll-view
this.calculateScrollViewHeight()
}, },
// //
@ -1666,24 +1691,32 @@ export default {
.schedule-main { .schedule-main {
flex: 1; flex: 1;
position: relative; position: relative;
display: flex;
overflow: hidden; overflow: hidden;
height: 0; /* 配合flex: 1 确保高度计算正确 */
} }
// //
.frozen-column { .schedule-container-inner {
width: 120rpx;
position: relative;
z-index: 3;
background-color: #292929;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-shadow: 4rpx 0 8rpx rgba(0, 0, 0, 0.1); min-width: 1380rpx; /* 左列120rpx + 7天 * 180rpx = 1380rpx */
flex-shrink: 0; }
//
.schedule-header-row {
display: flex;
background: #434544;
border-bottom: 2px solid #29d3b4;
position: sticky;
top: 0;
z-index: 10;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
} }
//
.time-header-cell { .time-header-cell {
width: 120rpx; width: 120rpx;
min-width: 120rpx;
min-height: 120rpx; min-height: 120rpx;
padding: 20rpx 10rpx; padding: 20rpx 10rpx;
color: #29d3b4; color: #29d3b4;
@ -1691,75 +1724,13 @@ export default {
font-weight: 500; font-weight: 500;
text-align: center; text-align: center;
border-right: 1px solid #555; border-right: 1px solid #555;
border-bottom: 2px solid #29d3b4;
background-color: #434544; background-color: #434544;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
} flex-shrink: 0;
.frozen-content-scroll {
flex: 1;
overflow: hidden;
/* 优化滚动性能 */
-webkit-overflow-scrolling: touch;
scroll-behavior: auto;
overscroll-behavior: none;
}
.frozen-content {
width: 100%;
}
.frozen-cell {
width: 120rpx;
min-height: 120rpx;
padding: 20rpx 10rpx;
color: #999;
font-size: 24rpx;
text-align: center;
border-right: 1px solid #434544;
border-bottom: 1px solid #434544;
background: #3a3a3a;
display: flex;
align-items: center;
justify-content: center;
word-wrap: break-word;
overflow-wrap: break-word;
&.time-unavailable {
background: #2a2a2a;
color: #555;
opacity: 0.5;
}
}
//
.schedule-content-area {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
//
.schedule-container-inner {
display: flex;
flex-direction: column;
min-width: 1260rpx; /* 7天 * 180rpx = 1260rpx */
}
.date-header-container {
display: flex;
background: #434544;
border-bottom: 2px solid #29d3b4;
position: sticky;
top: 0;
z-index: 10;
/* 确保完全覆盖下方内容 */
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
} }
.date-header-cell { .date-header-cell {
@ -1796,16 +1767,23 @@ export default {
// //
.schedule-scroll { .schedule-scroll {
flex: 1; flex: 1;
overflow: scroll; height: 100%;
width: 100%;
min-height: 0; /* 关键:允许flex子项收缩 */
/* 优化滚动性能 */ /* 优化滚动性能 */
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
scroll-behavior: auto; scroll-behavior: auto;
overscroll-behavior: none; overscroll-behavior: none;
/* 小程序端专用优化 */
// #ifdef MP-WEIXIN
scroll-behavior: smooth;
// #endif
} }
.schedule-grid { .schedule-grid {
width: 100%; width: 100%;
min-width: 1260rpx; /* 7天 * 180rpx = 1260rpx */ min-width: 1380rpx; /* 左列120rpx + 7天 * 180rpx = 1380rpx */
} }
// //
@ -1815,6 +1793,32 @@ export default {
border-bottom: 1px solid #434544; border-bottom: 1px solid #434544;
} }
// (///)
.left-column-cell {
width: 120rpx;
min-width: 120rpx;
min-height: 120rpx;
padding: 20rpx 10rpx;
color: #999;
font-size: 24rpx;
text-align: center;
border-right: 1px solid #434544;
border-bottom: 1px solid #434544;
background: #3a3a3a;
display: flex;
align-items: center;
justify-content: center;
word-wrap: break-word;
overflow-wrap: break-word;
flex-shrink: 0;
&.time-unavailable {
background: #2a2a2a;
color: #555;
opacity: 0.5;
}
}
.course-cell { .course-cell {
width: 180rpx; width: 180rpx;
min-width: 180rpx; min-width: 180rpx;
@ -1849,12 +1853,15 @@ export default {
// //
@media screen and (max-width: 375px) { @media screen and (max-width: 375px) {
.time-column-fixed { .left-column-cell {
width: 100rpx; width: 100rpx;
min-width: 100rpx;
font-size: 22rpx;
} }
.time-header-cell, .time-cell { .time-header-cell {
width: 100rpx; width: 100rpx;
min-width: 100rpx;
font-size: 22rpx; font-size: 22rpx;
} }

4
uniapp/pages-market/clue/add_clues.vue

@ -393,7 +393,7 @@
<view <view
class="input-title" class="input-title"
style="margin-right:14rpx;" style="margin-right:14rpx;"
@click="openDateTime(`promised_visit_time`)"> @click="openDate(`promised_visit_time`)">
{{ (formData.promised_visit_time) ? formData.promised_visit_time : '点击选择' }} {{ (formData.promised_visit_time) ? formData.promised_visit_time : '点击选择' }}
</view> </view>
</view> </view>
@ -1671,7 +1671,7 @@ export default {
// //
this.$nextTick(() => { this.$nextTick(() => {
this.datetime_picker_show = true this.date_picker_show = true
}) })
}, },
// //

12
uniapp/pages/common/home/index.vue

@ -106,12 +106,12 @@
path: '/pages/common/dashboard/webview', path: '/pages/common/dashboard/webview',
params: { type: 'campus_data' } params: { type: 'campus_data' }
}, },
{ // {
title: '报销管理', // title: '',
icon: 'wallet-filled', // icon: 'wallet-filled',
path: '/pages-market/reimbursement/list', // path: '/pages-market/reimbursement/list',
params: { type: 'reimbursement' } // params: { type: 'reimbursement' }
} // }
] ]
} }
}, },

Loading…
Cancel
Save