Browse Source

临时保存

master
王泽彦 10 months ago
parent
commit
3033834c4b
  1. 7
      admin/components.d.ts
  2. 484
      admin/src/app/views/timetables/timetables.vue
  3. 7
      niucloud/app/adminapi/controller/course_schedule/CourseSchedule.php
  4. 2
      niucloud/app/adminapi/route/course_schedule.php
  5. 13
      niucloud/app/service/admin/course_schedule/CourseScheduleService.php

7
admin/components.d.ts

@ -17,7 +17,10 @@ declare module '@vue/runtime-core' {
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard'] ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol'] ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']
@ -41,6 +44,8 @@ declare module '@vue/runtime-core' {
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination'] ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover'] ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow'] ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelect: typeof import('element-plus/es')['ElSelect']
@ -50,7 +55,9 @@ declare module '@vue/runtime-core' {
ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs'] ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload'] ElUpload: typeof import('element-plus/es')['ElUpload']
ExportSure: typeof import('./src/components/export-sure/index.vue')['default'] ExportSure: typeof import('./src/components/export-sure/index.vue')['default']
HeatMap: typeof import('./src/components/heat-map/index.vue')['default'] HeatMap: typeof import('./src/components/heat-map/index.vue')['default']

484
admin/src/app/views/timetables/timetables.vue

@ -12,23 +12,21 @@
<el-option <el-option
v-for="item in campusList" v-for="item in campusList"
:key="item.id" :key="item.id"
:label="item.name" :label="item.campus_name"
:value="item.id" :value="item.id"
/> />
</el-select> </el-select>
<el-button @click="prevWeek" icon="el-icon-arrow-left">上一周</el-button>
<el-date-picker <el-date-picker
v-model="dateRange" v-model="weekDate"
type="daterange" type="week"
range-separator="至" format="YYYY 第 ww 周"
start-placeholder="开始日期" placeholder="选择周"
end-placeholder="结束日期" class="week-picker"
format="YYYY-MM-DD" @change="handleWeekChange"
value-format="YYYY-MM-DD"
@change="handleDateRangeChange"
/> />
<el-button type="primary" class="ml-2" @click="fetchData" <el-button @click="nextWeek" icon="el-icon-arrow-right">下一周</el-button>
>查询</el-button <el-button type="primary" class="ml-2" @click="fetchData">查询</el-button>
>
</div> </div>
<el-button @click="addSchedule">添加课程</el-button> <el-button @click="addSchedule">添加课程</el-button>
</div> </div>
@ -102,54 +100,243 @@
}} }}
</p> </p>
</el-dialog> </el-dialog>
<!-- 添加课程弹窗 -->
<el-dialog v-model="addDialogVisible" title="添加课程" width="600px">
<el-form
ref="addFormRef"
:model="addForm"
:rules="addFormRules"
label-width="120px"
>
<el-form-item label="校区" prop="campus_id">
<el-select
v-model="addForm.campus_id"
placeholder="请选择校区"
@change="handleAddCampusChange"
>
<el-option
v-for="item in campusList"
:key="item.id"
:label="item.campus_name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="场地" prop="venue_id">
<el-select
v-model="addForm.venue_id"
placeholder="请选择场地"
@change="calculateAvailableCapacity"
>
<el-option
v-for="item in venueList"
:key="item.id"
:label="item.venue_name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="上课日期" prop="course_date">
<el-date-picker
v-model="addForm.course_date"
type="date"
placeholder="选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="上课时段" prop="time_slot">
<el-select
v-model="addForm.time_slot"
placeholder="请选择上课时段"
@change="calculateAvailableCapacity"
>
<el-option
v-for="(timeSlot, index) in timeSlotOptions"
:key="index"
:label="timeSlot"
:value="timeSlot"
/>
</el-select>
</el-form-item>
<el-form-item label="剩余空位" v-if="addForm.venue_id && addForm.time_slot">
<span>{{ availableCapacity }}</span>
</el-form-item>
<el-form-item label="上课类型" prop="course_type">
<el-radio-group v-model="addForm.course_type" @change="handleCourseTypeChange">
<el-radio label="class">班级</el-radio>
<el-radio label="student">学员</el-radio>
<el-radio label="trial">试课</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="班级" prop="class_ids" v-if="addForm.course_type === 'class'">
<el-checkbox-group v-model="addForm.class_ids" @change="handleClassChange">
<el-checkbox
v-for="item in classList"
:key="item.id"
:label="item.id"
>
{{ item.class_name }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="学员" prop="student_ids" v-if="addForm.course_type === 'student' || addForm.course_type === 'trial'">
<el-input
v-model="studentSearchKeyword"
placeholder="搜索学员名称"
@input="searchStudents"
/>
<div class="student-checkbox-list">
<el-checkbox-group v-model="addForm.student_ids">
<el-checkbox
v-for="item in filteredStudentList"
:key="item.id"
:label="item.id"
>
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</el-form-item>
<el-form-item label="课程名称" prop="course_name">
<el-input v-model="addForm.course_name" placeholder="请输入课程名称" />
</el-form-item>
<el-form-item label="上课教练" prop="coach_id">
<el-select v-model="addForm.coach_id" placeholder="请选择教练">
<el-option
v-for="item in coachList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitAddForm">确定</el-button>
</template>
</el-dialog>
</div> </div>
</el-card> </el-card>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted, computed } from 'vue'
import { getTimetables } from '@/app/api/course_schedule' import { getTimetables } from '@/app/api/course_schedule'
import { getCampusList } from '@/app/api/campus' import { getWithCampusList } from '@/app/api/venue'
import { getVenueList } from '@/app/api/venue'
import { getClassroomList, getWithPersonnelList } from '@/app/api/classroom'
import { addCourseSchedule } from '@/app/api/course_schedule'
import { ElMessage } from 'element-plus'
// //
const campusList = ref([]) const campusList = ref([])
const selectedCampus = ref('') const selectedCampus = ref('')
// //
const dateRange = ref([ const addDialogVisible = ref(false)
getMonday(new Date()).toISOString().split('T')[0], const addFormRef = ref(null)
getSunday(new Date()).toISOString().split('T')[0], const venueList = ref([])
]) const classList = ref([])
const studentList = ref([])
const coachList = ref([])
const timeSlotOptions = ref(['9:00-10:00', '10:00-11:00', '11:00-12:00', '14:00-15:00', '15:00-16:00', '16:00-17:00'])
const availableCapacity = ref(0)
const studentSearchKeyword = ref('')
const filteredStudentList = ref([])
const addForm = ref({
campus_id: '',
venue_id: '',
course_date: '',
time_slot: '',
course_type: 'class',
class_ids: [],
student_ids: [],
course_name: '',
coach_id: ''
})
const addFormRules = {
campus_id: [{ required: true, message: '请选择校区', trigger: 'change' }],
venue_id: [{ required: true, message: '请选择场地', trigger: 'change' }],
course_date: [{ required: true, message: '请选择上课日期', trigger: 'change' }],
time_slot: [{ required: true, message: '请选择上课时段', trigger: 'change' }],
course_type: [{ required: true, message: '请选择上课类型', trigger: 'change' }],
coach_id: [{ required: true, message: '请选择上课教练', trigger: 'change' }],
course_name: [{ required: true, message: '请输入课程名称', trigger: 'blur' }]
}
//
const weekDate = ref(new Date())
//
const dateRange = computed(() => {
if (!weekDate.value) return [null, null]
//
const date = new Date(weekDate.value)
const day = date.getDay() || 7
//
date.setDate(date.getDate() - day + 1)
const monday = new Date(date)
//
date.setDate(date.getDate() + 6)
const sunday = new Date(date)
return [
monday.toISOString().split('T')[0],
sunday.toISOString().split('T')[0]
]
})
// //
const days = ref([]) const days = ref([])
const dialogVisible = ref(false) const dialogVisible = ref(false)
const selectedCourse = ref(null) const selectedCourse = ref(null)
// //
function getMonday(date) { const prevWeek = () => {
const day = date.getDay() || 7 const date = new Date(weekDate.value)
if (day !== 1) { date.setDate(date.getDate() - 7)
date.setHours(-24 * (day - 1)) weekDate.value = date
} fetchData()
return date
} }
// //
function getSunday(date) { const nextWeek = () => {
const day = date.getDay() || 7 const date = new Date(weekDate.value)
if (day !== 0) { date.setDate(date.getDate() + 7)
date.setHours(24 * (7 - day)) weekDate.value = date
} fetchData()
return date }
//
const handleWeekChange = () => {
fetchData()
}
//
const handleCampusChange = () => {
fetchData()
} }
// //
const fetchCampusList = async () => { const fetchCampusList = async () => {
try { try {
const response = await getCampusList({}) const response = await getWithCampusList({})
if (response.data && response.data.list) { if (response.data) {
campusList.value = response.data.list campusList.value = response.data
// //
if (campusList.value.length > 0) { if (campusList.value.length > 0) {
selectedCampus.value = campusList.value[0].id selectedCampus.value = campusList.value[0].id
@ -160,16 +347,6 @@ const fetchCampusList = async () => {
} }
} }
//
const handleCampusChange = (val) => {
selectedCampus.value = val
}
//
const handleDateRangeChange = (val) => {
dateRange.value = val
}
// //
const fetchData = async () => { const fetchData = async () => {
try { try {
@ -206,8 +383,7 @@ const getStudentName = (studentId) => {
// //
const addSchedule = () => { const addSchedule = () => {
// addDialogVisible.value = true
console.log('添加课程')
} }
// //
@ -249,10 +425,209 @@ const handleCellClick = (row, column, cell, event) => {
} }
} }
//
const handleAddCampusChange = async () => {
//
try {
const response = await getVenueList({ campus_id: addForm.value.campus_id })
if (response.data && response.data.list) {
venueList.value = response.data.list
}
} catch (error) {
console.error('获取场地列表失败:', error)
}
//
try {
const response = await getClassroomList({ campus_id: addForm.value.campus_id })
if (response.data && response.data.list) {
classList.value = response.data.list
}
} catch (error) {
console.error('获取班级列表失败:', error)
}
//
addForm.value.venue_id = ''
addForm.value.class_ids = []
availableCapacity.value = 0
}
const calculateAvailableCapacity = async () => {
if (!addForm.value.venue_id || !addForm.value.time_slot) {
availableCapacity.value = 0
return
}
try {
//
const venueInfo = venueList.value.find(item => item.id === addForm.value.venue_id)
if (venueInfo) {
//
availableCapacity.value = venueInfo.capacity || 0
//
if (addForm.value.course_date) {
const params = {
venue_id: addForm.value.venue_id,
course_date: addForm.value.course_date,
time_slot: addForm.value.time_slot
}
const response = await getTimetables(params)
if (response.data && response.data.length > 0) {
//
const matchingDay = response.data.find(day =>
day.date.includes(addForm.value.course_date)
)
if (matchingDay) {
const matchingTimeSlot = matchingDay.timeSlots.find(slot =>
slot.timeRange === addForm.value.time_slot
)
if (matchingTimeSlot && matchingTimeSlot.course) {
//
availableCapacity.value = matchingTimeSlot.course.hasnumber
}
}
}
}
}
} catch (error) {
console.error('计算可用容量失败:', error)
}
}
const handleCourseTypeChange = () => {
//
addForm.value.class_ids = []
addForm.value.student_ids = []
addForm.value.course_name = ''
//
if (addForm.value.course_type === 'student' || addForm.value.course_type === 'trial') {
loadStudentList()
}
}
const handleClassChange = () => {
//
if (addForm.value.class_ids.length > 0) {
const selectedClass = classList.value.find(item => item.id === addForm.value.class_ids[0])
if (selectedClass) {
addForm.value.course_name = selectedClass.class_name
}
}
}
//
const loadStudentList = async () => {
try {
//
// 使
// const response = await fetch('/student_courses/student_all')
// const data = await response.json()
// if (data.data) {
// studentList.value = data.data
// filteredStudentList.value = [...studentList.value]
// }
//
studentList.value = [
{ id: 1, name: '学员1' },
{ id: 2, name: '学员2' },
{ id: 3, name: '学员3' }
]
filteredStudentList.value = [...studentList.value]
} catch (error) {
console.error('获取学员列表失败:', error)
}
}
//
const loadCoachList = async () => {
try {
// 使classroom
const response = await getWithPersonnelList({})
if (response.data) {
coachList.value = response.data
} else {
//
coachList.value = [
{ id: 1, name: '教练1' },
{ id: 2, name: '教练2' },
{ id: 3, name: '教练3' }
]
}
} catch (error) {
console.error('获取教练列表失败:', error)
//
coachList.value = [
{ id: 1, name: '教练1' },
{ id: 2, name: '教练2' },
{ id: 3, name: '教练3' }
]
}
}
const searchStudents = () => {
if (!studentSearchKeyword.value) {
filteredStudentList.value = [...studentList.value]
return
}
filteredStudentList.value = studentList.value.filter(student =>
student.name.includes(studentSearchKeyword.value)
)
}
const submitAddForm = () => {
addFormRef.value.validate(async (valid) => {
if (!valid) return
try {
const params = {
campus_id: addForm.value.campus_id,
venue_id: addForm.value.venue_id,
course_date: addForm.value.course_date,
time_slot: addForm.value.time_slot,
course_id: 0, //
coach_id: addForm.value.coach_id,
participants: addForm.value.course_type,
available_capacity: availableCapacity.value,
status: 'active',
}
//
if (addForm.value.course_type === 'class') {
params.class_ids = addForm.value.class_ids
//
const classStudents = []
//
params.student_ids = classStudents
} else {
params.student_ids = addForm.value.student_ids
}
const response = await addCourseSchedule(params)
if (response.code === 0) {
ElMessage.success('添加课程成功')
addDialogVisible.value = false
fetchData() //
}
} catch (error) {
console.error('添加课程失败:', error)
ElMessage.error('添加课程失败')
}
})
}
// //
onMounted(() => { onMounted(() => {
fetchCampusList() fetchCampusList()
fetchData() fetchData()
loadCoachList()
}) })
</script> </script>
@ -292,4 +667,17 @@ onMounted(() => {
.classroom-name { .classroom-name {
margin-bottom: 5px; margin-bottom: 5px;
} }
.week-picker {
width: 180px;
}
.student-checkbox-list {
max-height: 200px;
overflow-y: auto;
border: 1px solid #EBEEF5;
border-radius: 4px;
padding: 10px;
margin-top: 8px;
}
</style> </style>

7
niucloud/app/adminapi/controller/course_schedule/CourseSchedule.php

@ -123,4 +123,11 @@ class CourseSchedule extends BaseAdminController
]); ]);
return success((new CourseScheduleService())->getTimetables($data)); return success((new CourseScheduleService())->getTimetables($data));
} }
public function getCampusVenue()
{
$data = $this->request->params([
["campus_id", ""],
]);
return success((new CourseScheduleService())->getCampusVenue($data));
}
} }

2
niucloud/app/adminapi/route/course_schedule.php

@ -29,6 +29,8 @@ Route::group('course_schedule', function () {
Route::put('course_schedule/:id', 'course_schedule.CourseSchedule/edit'); Route::put('course_schedule/:id', 'course_schedule.CourseSchedule/edit');
//删除课程安排 //删除课程安排
Route::delete('course_schedule/:id', 'course_schedule.CourseSchedule/del'); Route::delete('course_schedule/:id', 'course_schedule.CourseSchedule/del');
//获取校区下的场地
Route::get('campus_venue', 'course_schedule.CourseSchedule/getCampusVenue');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,

13
niucloud/app/service/admin/course_schedule/CourseScheduleService.php

@ -122,14 +122,6 @@ class CourseScheduleService extends BaseAdminService
// 查询指定日期范围内的课程安排 // 查询指定日期范围内的课程安排
$schedules = $this->model->where($query_condition)->select()->toArray(); $schedules = $this->model->where($query_condition)->select()->toArray();
// 检查是否需要自动创建课程安排记录
$need_auto_create = empty($schedules) && $campus_id > 0;
// 如果需要自动创建记录
if ($need_auto_create) {
$schedules = $this->autoCreateSchedules($start_date, $end_date, $campus_id);
}
// 获取所有相关的人员课程安排关系 // 获取所有相关的人员课程安排关系
$schedule_ids = array_column($schedules, 'id'); $schedule_ids = array_column($schedules, 'id');
$person_schedules = []; $person_schedules = [];
@ -279,4 +271,9 @@ class CourseScheduleService extends BaseAdminService
return $schedules; return $schedules;
} }
public function getCampusVenue($id)
{
return $this->model->where('availability_status', 1)->where('campus_id',$id)->select();
}
} }

Loading…
Cancel
Save