Browse Source

修改 bug

master
王泽彦 7 months ago
parent
commit
e3b3237721
  1. BIN
      doc/副本课程协议—月卡篮球(1).docx
  2. 4
      uniapp/common/config.js
  3. 539
      uniapp/pages-coach/coach/schedule/schedule_table.vue
  4. 10
      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 = 'prod'
// const env = 'development'
const env = 'prod'
const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退
const isDebug = false // 默认启用调试模式
const devurl = 'http://localhost:20080/api'

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

@ -56,111 +56,45 @@
<!-- 课程表主体 -->
<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>
<!-- 统一滚动区域 -->
<scroll-view
class="schedule-scroll"
scroll-x
scroll-y
:style="{ height: scrollViewHeight + 'px' }"
:enable-flex="true"
:enhanced="isH5"
:scroll-anchoring="false"
:bounces="false"
:scroll-with-animation="false"
:show-scrollbar="false"
@scroll="onScroll"
>
<view class="schedule-container-inner" :style="{ width: (tableWidth + 120) + 'rpx', minWidth: '1380rpx' }">
<!-- 表头行 -->
<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 class="schedule-content-area">
<!-- 合并的滚动区域表头+内容 -->
<scroll-view
class="schedule-scroll"
scroll-x
scroll-y
:scroll-top="scrollTop"
:enable-flex="true"
:scroll-anchoring="false"
:enhanced="true"
:bounces="false"
:scroll-with-animation="false"
@scroll="onScroll"
>
<view class="schedule-container-inner" :style="{ width: tableWidth + 'rpx', minWidth: '1260rpx' }">
<!-- 表头 -->
<view class="date-header-container">
<!-- 日期列 -->
<view
class="date-header-cell"
v-for="(date, index) in weekDates"
:key="index"
>
<view class="date-week">{{ date.weekName }}</view>
<view class="date-day">{{ date.dateStr }}</view>
<view class="date-courses">{{ date.courseCount }}节课</view>
</view>
<!-- 日期列 -->
<view
class="date-header-cell"
v-for="(date, index) in weekDates"
:key="index"
>
<view class="date-week">{{ date.weekName }}</view>
<view class="date-day">{{ date.dateStr }}</view>
<view class="date-courses">{{ date.courseCount }}节课</view>
</view>
</view>
<!-- 内容网格 -->
<view class="schedule-grid">
<!-- 内容网格 -->
<view class="schedule-grid">
<!-- 时间模式内容 -->
<template v-if="activeFilter === 'time' || activeFilter === ''">
<view
@ -169,6 +103,12 @@
:key="timeIndex"
:ref="`scheduleRow_${timeIndex}`"
>
<!-- 左侧时间列 -->
<view :class="['left-column-cell', !timeSlot.available ? 'time-unavailable' : '']">
{{ timeSlot.time }}
</view>
<!-- 课程单元格 -->
<view
:class="['course-cell',!timeSlot.available ? 'cell-unavailable' : '']"
v-for="(date, dateIndex) in weekDates"
@ -234,6 +174,12 @@
:key="teacher.id"
:ref="`scheduleRow_${teacherIndex}`"
>
<!-- 左侧教练列 -->
<view class="left-column-cell">
{{ teacher.name }}
</view>
<!-- 课程单元格 -->
<view
class="course-cell"
v-for="(date, dateIndex) in weekDates"
@ -299,6 +245,12 @@
:key="venue.id"
:ref="`scheduleRow_${venueIndex}`"
>
<!-- 左侧教室列 -->
<view class="left-column-cell">
{{ venue.name }}
</view>
<!-- 课程单元格 -->
<view
class="course-cell"
v-for="(date, dateIndex) in weekDates"
@ -364,6 +316,12 @@
:key="cls.id"
:ref="`scheduleRow_${clsIndex}`"
>
<!-- 左侧班级列 -->
<view class="left-column-cell">
{{ cls.name }}
</view>
<!-- 课程单元格 -->
<view
class="course-cell"
v-for="(date, dateIndex) in weekDates"
@ -420,10 +378,9 @@
</view>
</view>
</template>
</view>
</view>
</scroll-view>
</view>
</view>
</scroll-view>
</view>
<!-- 筛选弹窗 -->
@ -554,11 +511,10 @@ export default {
selectedClasses: [],
//
scrollTop: 0,
scrollTimer: null, //
//
tableWidth: 1500, // 7 (7*180+120=1380rpx)
// -
tableWidth: 1260, // 7 (7*180=1260rpx)120rpx
//
timeSlots: [],
@ -591,6 +547,12 @@ export default {
selectedScheduleId: null,
showScheduleDetail: false,
//
isH5: false,
//
scrollViewHeight: 0,
//
filterParams: {
start_date: '',
@ -644,12 +606,20 @@ export default {
},
mounted() {
//
// #ifdef H5
this.isH5 = true
// #endif
this.initCurrentWeek()
this.initTimeSlots()
//
this.handleResize()
// scroll-view
this.calculateScrollViewHeight()
//
this.loadFilterOptions().then(() => {
this.loadScheduleList()
@ -679,6 +649,72 @@ export default {
},
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() {
const today = new Date()
@ -857,8 +893,8 @@ export default {
getCoursesByTimeAndDate(time, date) {
const matchedCourses = this.courses.filter(course => {
if (course.date !== date) return false
//
return course.time === time
// 使
return this.isTimeInSlot(course.time, time)
})
return matchedCourses
},
@ -933,8 +969,7 @@ export default {
this.filterParams.venue_id = '';
this.filterParams.class_id = '';
//
this.scrollTop = 0;
//
//
this.loadScheduleList();
@ -956,9 +991,8 @@ export default {
this.applyFilters()
this.closeFilterModal()
//
//
await this.loadScheduleList()
this.scrollTop = 0
//
if (this.selectedTimeRange !== '' || this.selectedVenueId !== null) {
@ -1146,34 +1180,35 @@ export default {
if (res.code === 1) {
//
this.courses = res.data.list.map(item => ({
id: item.id,
date: item.course_date,
time: item.time_info?.start_time || item.time_slot?.split('-')[0],
courseName: item.course_name || '未命名课程',
students: `已报名${item.enrolled_count || 0}`,
teacher: item.coach_name || '待分配',
teacher_id: item.coach_id, // ID
status: item.status_text || '待定',
type: this.getCourseType(item),
venue: item.venue_name || '待分配',
venue_id: item.venue_id, // ID
campus_name: item.campus_name || '', //
class_id: item.class_id, // ID
class_name: item.class_name || '', //
duration: item.time_info?.duration || 60,
time_slot: item.time_slot,
raw: 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,
date: item.course_date,
time: normalizedTime, // 使
courseName: item.course_name || '未命名课程',
students: `已报名${item.enrolled_count || 0}`,
teacher: item.coach_name || '待分配',
teacher_id: item.coach_id, // ID
status: item.status_text || '待定',
type: this.getCourseType(item),
venue: item.venue_name || '待分配',
venue_id: item.venue_id, // ID
campus_name: item.campus_name || '', //
class_id: item.class_id, // ID
class_name: item.class_name || '', //
duration: item.time_info?.duration || 60,
time_slot: item.time_slot,
raw: item, //
}
})
//
this.updateLeftColumnData();
//
this.$nextTick(() => {
this.syncRowHeights();
});
} else {
uni.showToast({
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) {
// 使
//
//
if (this.scrollTimer) {
clearTimeout(this.scrollTimer)
}
this.scrollTimer = setTimeout(() => {
//
if (e.detail.scrollTop !== undefined && e.detail.scrollTop !== this.scrollTop) {
this.scrollTop = e.detail.scrollTop
}
}, 16) // 60fps
//
// /
}, 16)
},
//
@ -1523,6 +1515,36 @@ export default {
// 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() {
//
@ -1545,12 +1567,15 @@ export default {
// #endif
if (width <= 375) {
this.tableWidth = 1220 // 7*160+100=1220rpx
this.tableWidth = 1120 // 7*160=1120rpx (160rpx)
} else if (width <= 768) {
this.tableWidth = 1400
this.tableWidth = 1260 // 7*180=1260rpx
} else {
this.tableWidth = 1500
this.tableWidth = 1260 // 7*180=1260rpx
}
// scroll-view
this.calculateScrollViewHeight()
},
//
@ -1666,24 +1691,32 @@ export default {
.schedule-main {
flex: 1;
position: relative;
display: flex;
overflow: hidden;
height: 0; /* 配合flex: 1 确保高度计算正确 */
}
//
.frozen-column {
width: 120rpx;
position: relative;
z-index: 3;
background-color: #292929;
//
.schedule-container-inner {
display: flex;
flex-direction: column;
box-shadow: 4rpx 0 8rpx rgba(0, 0, 0, 0.1);
flex-shrink: 0;
min-width: 1380rpx; /* 左列120rpx + 7天 * 180rpx = 1380rpx */
}
//
.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 {
width: 120rpx;
min-width: 120rpx;
min-height: 120rpx;
padding: 20rpx 10rpx;
color: #29d3b4;
@ -1691,75 +1724,13 @@ export default {
font-weight: 500;
text-align: center;
border-right: 1px solid #555;
border-bottom: 2px solid #29d3b4;
background-color: #434544;
display: flex;
align-items: center;
justify-content: center;
word-wrap: break-word;
overflow-wrap: break-word;
}
.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);
flex-shrink: 0;
}
.date-header-cell {
@ -1796,16 +1767,23 @@ export default {
//
.schedule-scroll {
flex: 1;
overflow: scroll;
height: 100%;
width: 100%;
min-height: 0; /* 关键:允许flex子项收缩 */
/* 优化滚动性能 */
-webkit-overflow-scrolling: touch;
scroll-behavior: auto;
overscroll-behavior: none;
/* 小程序端专用优化 */
// #ifdef MP-WEIXIN
scroll-behavior: smooth;
// #endif
}
.schedule-grid {
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;
}
// (///)
.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 {
width: 180rpx;
min-width: 180rpx;
@ -1849,12 +1853,15 @@ export default {
//
@media screen and (max-width: 375px) {
.time-column-fixed {
.left-column-cell {
width: 100rpx;
min-width: 100rpx;
font-size: 22rpx;
}
.time-header-cell, .time-cell {
.time-header-cell {
width: 100rpx;
min-width: 100rpx;
font-size: 22rpx;
}

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

@ -353,7 +353,7 @@
<!--可选上课时间-->
<fui-form-item
label="1、可选上课时间"
label="1、 可选上课时间"
labelWidth="240"
labelSize='26'
prop=""
@ -381,7 +381,7 @@
</fui-form-item>
<!--承诺到访时间-->
<fui-form-item
label="2、承诺到访时间"
label="2、 承诺到访时间"
labelSize='26'
labelWidth="240"
prop=""
@ -393,14 +393,14 @@
<view
class="input-title"
style="margin-right:14rpx;"
@click="openDateTime(`promised_visit_time`)">
@click="openDate(`promised_visit_time`)">
{{ (formData.promised_visit_time) ? formData.promised_visit_time : '点击选择' }}
</view>
</view>
</fui-form-item>
<!--距离-->
<fui-form-item
label="3、距离"
label="3、 距离"
labelSize='26'
label-width="210"
prop="title"
@ -1671,7 +1671,7 @@ export default {
//
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',
params: { type: 'campus_data' }
},
{
title: '报销管理',
icon: 'wallet-filled',
path: '/pages-market/reimbursement/list',
params: { type: 'reimbursement' }
}
// {
// title: '',
// icon: 'wallet-filled',
// path: '/pages-market/reimbursement/list',
// params: { type: 'reimbursement' }
// }
]
}
},

Loading…
Cancel
Save