Browse Source

本地临时保存

master
王泽彦 10 months ago
parent
commit
5f4acb2dfa
  1. 8
      admin/components.d.ts
  2. 9
      admin/src/app/api/course_schedule.ts
  3. 35
      admin/src/app/views/classroom/classroom.vue
  4. 1
      admin/src/app/views/classroom/components/classroom-edit.vue
  5. 204
      admin/src/app/views/timetables/timetables.vue
  6. 20
      niucloud/app/adminapi/controller/course_schedule/CourseSchedule.php
  7. 187
      niucloud/app/service/admin/course_schedule/CourseScheduleService.php

8
admin/components.d.ts

@ -18,6 +18,8 @@ declare module '@vue/runtime-core' {
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
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']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer']
@ -39,8 +41,11 @@ declare module '@vue/runtime-core' {
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
ElPagination: typeof import('element-plus/es')['ElPagination']
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']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
@ -50,7 +55,10 @@ declare module '@vue/runtime-core' {
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTimeSelect: typeof import('element-plus/es')['ElTimeSelect']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload']
ExportSure: typeof import('./src/components/export-sure/index.vue')['default']
HeatMap: typeof import('./src/components/heat-map/index.vue')['default']

9
admin/src/app/api/course_schedule.ts

@ -56,4 +56,13 @@ export function deleteCourseSchedule(id: number) {
})
}
/**
*
* @param params
* @returns
*/
export function getTimetables(params: Record<string, any>) {
return request.get(`course_schedule/course_schedule/timetables`, { params })
}
// USER_CODE_END -- course_schedule

35
admin/src/app/views/classroom/classroom.vue

@ -114,12 +114,12 @@
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadClassroomList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadClassroomList()"
>{{ t('search') }}
</el-button>
<el-button @click="resetForm(searchFormRef)"
>{{ t('reset') }}
</el-button>
</el-form-item>
</el-form>
</el-card>
@ -158,6 +158,7 @@
prop="age_group"
:label="t('ageGroup')"
min-width="120"
align="center"
:show-overflow-tooltip="true"
/>
@ -179,7 +180,13 @@
:label="t('assistantCoach')"
min-width="120"
:show-overflow-tooltip="true"
/>
>
<template #default="{ row }">
<div v-for="(item, index) in assistantCoachList">
<div v-if="item.id == row.assistant_coach">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column
:label="t('status')"
@ -198,6 +205,7 @@
prop="sort_order"
:label="t('sortOrder')"
min-width="120"
align="center"
:show-overflow-tooltip="true"
/>
@ -207,12 +215,12 @@
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
<el-button type="primary" link @click="editEvent(row)"
>{{ t('edit') }}
</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)"
>{{ t('delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
@ -246,6 +254,7 @@ import {
import { ElMessageBox, FormInstance } from 'element-plus'
import Edit from '@/app/views/classroom/components/classroom-edit.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title

1
admin/src/app/views/classroom/components/classroom-edit.vue

@ -60,6 +60,7 @@
<el-form-item :label="t('ageGroup')">
<el-input
v-model="formData.age_group"
type="number"
clearable
:placeholder="t('ageGroupPlaceholder')"
class="input-width"

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

@ -1,6 +1,36 @@
<template>
<el-card class="box-card !border-none" shadow="never">
<el-button @click="addDict">添加</el-button>
<div class="mb-4 flex items-center justify-between">
<div class="flex items-center">
<el-select
v-model="selectedCampus"
placeholder="请选择校区"
clearable
class="mr-2"
@change="handleCampusChange"
>
<el-option
v-for="item in campusList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
@change="handleDateRangeChange"
/>
<el-button type="primary" class="ml-2" @click="fetchData">查询</el-button>
</div>
<el-button @click="addSchedule">添加课程</el-button>
</div>
<div class="schedule-container">
<!-- 周一到周日的布局 -->
<div v-for="(day, index) in days" :key="index" class="day-column">
@ -30,13 +60,13 @@
<el-table-column
v-for="(classroom, idx) in day.classrooms"
:key="idx"
:label="`教室${idx + 1}`"
:prop="`classroom${idx + 1}`"
:label="`教室${classroom}`"
:prop="`classroom${classroom}`"
align="center"
>
<template #default="{ row }">
<div v-if="row.course && row.course.classroom === classroom">
<div class="teacher-name">{{ row.course.teacher }}</div>
<div v-if="row.course && row.course.classroom == classroom">
<div class="teacher-name">{{ getTeacherName(row.course.teacher) }}</div>
<div class="student-list">
<el-tag
v-for="student in row.course.students"
@ -44,7 +74,7 @@
size="small"
effect="plain"
>
{{ student }}
{{ getStudentName(student) }}
</el-tag>
</div>
<div class="classroom-name">
@ -58,67 +88,117 @@
<!-- 详情弹窗 -->
<el-dialog v-model="dialogVisible" title="课程详情">
<p><strong>教师:</strong> {{ selectedCourse?.teacher }}</p>
<p><strong>学员:</strong> {{ selectedCourse?.students.join(', ') }}</p>
<p><strong>教师:</strong> {{ getTeacherName(selectedCourse?.teacher) }}</p>
<p><strong>学员:</strong> {{ selectedCourse?.students.map(id => getStudentName(id)).join(', ') }}</p>
</el-dialog>
</div>
</el-card>
</template>
<script setup>
import { ref } from 'vue'
const days = [
//
{
date: '周一',
timeSlots: [
{
timeRange: '9:00-10:00',
color: '#FFA07A',
course: {
teacher: '',
students: [],
classroom: '教室1',
hasnumber: 5,
},
},
// ...
],
classrooms: ['教室1'], //
},
{
date: '周二',
timeSlots: [
{
timeRange: '9:00-10:00',
color: '#FFA07A',
course: {
teacher: '张老师',
students: ['小明', '小红'],
classroom: '教室1',
hasnumber: 5,
},
},
{
timeRange: '11:00-12:00',
color: '#712e13',
course: {
teacher: '张老师',
students: ['小明', '小红'],
classroom: '教室2',
hasnumber: 5,
},
},
// ...
],
classrooms: ['教室1', '教室2'], //
},
]
import { ref, onMounted } from 'vue'
import { getTimetables } from '@/app/api/course_schedule'
import { getCampusList } from '@/app/api/campus'
//
const campusList = ref([])
const selectedCampus = ref('')
//
const dateRange = ref([
getMonday(new Date()).toISOString().split('T')[0],
getSunday(new Date()).toISOString().split('T')[0]
])
//
const days = ref([])
const dialogVisible = ref(false)
const selectedCourse = ref(null)
//
function getMonday(date) {
const day = date.getDay() || 7
if (day !== 1) {
date.setHours(-24 * (day - 1))
}
return date
}
//
function getSunday(date) {
const day = date.getDay() || 7
if (day !== 0) {
date.setHours(24 * (7 - day))
}
return date
}
//
const fetchCampusList = async () => {
try {
const response = await getCampusList({})
if (response.data && response.data.list) {
campusList.value = response.data.list
//
if (campusList.value.length > 0) {
selectedCampus.value = campusList.value[0].id
}
}
} catch (error) {
console.error('获取校区列表失败:', error)
}
}
//
const handleCampusChange = (val) => {
selectedCampus.value = val
}
//
const handleDateRangeChange = (val) => {
dateRange.value = val
}
//
const fetchData = async () => {
try {
const params = {}
if (dateRange.value && dateRange.value.length === 2) {
params.start_date = dateRange.value[0]
params.end_date = dateRange.value[1]
}
if (selectedCampus.value) {
params.campus_id = selectedCampus.value
}
const response = await getTimetables(params)
if (response.data) {
days.value = response.data
}
} catch (error) {
console.error('获取课程表数据失败:', error)
}
}
//
const getTeacherName = (teacherId) => {
// teacherId
return teacherId ? `教师${teacherId}` : ''
}
//
const getStudentName = (studentId) => {
// studentId
return studentId ? `学生${studentId}` : ''
}
//
const addSchedule = () => {
//
console.log('添加课程')
}
//
const objectSpanMethod = (timeSlots, { row, column, rowIndex }) => {
if (column.property === 'timeRange') {
@ -152,18 +232,24 @@ const objectSpanMethod = (timeSlots, { row, column, rowIndex }) => {
//
const handleCellClick = (row, column, cell, event) => {
console.log(row, column, cell, event)
if (column.property.startsWith('classroom')) {
if (column.property.startsWith('classroom') && row.course) {
selectedCourse.value = row.course
dialogVisible.value = true
}
}
//
onMounted(() => {
fetchCampusList()
fetchData()
})
</script>
<style scoped>
.schedule-container {
display: flex;
gap: 10px;
overflow-x: auto;
}
.day-column {

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

@ -44,10 +44,12 @@ class CourseSchedule extends BaseAdminController
/**
* 课程安排详情
* @param int $id
* @param mixed $id
* @return \think\Response
*/
public function info(int $id){
public function info($id){
// 确保 $id 是整数类型
$id = intval($id);
return success((new CourseScheduleService())->getInfo($id));
}
@ -107,6 +109,18 @@ class CourseSchedule extends BaseAdminController
(new CourseScheduleService())->del($id);
return success('DELETE_SUCCESS');
}
/**
* 获取课程表数据
* @return \think\Response
*/
public function timetables(){
$data = $this->request->params([
["start_date", ""],
["end_date", ""],
["campus_id", ""],
["venue_id", ""]
]);
return success((new CourseScheduleService())->getTimetables($data));
}
}

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

@ -12,7 +12,7 @@
namespace app\service\admin\course_schedule;
use app\model\course_schedule\CourseSchedule;
use app\model\person_course_schedule\PersonCourseSchedule;
use core\base\BaseAdminService;
@ -94,6 +94,189 @@ class CourseScheduleService extends BaseAdminService
return $res;
}
/**
* 获取课程表数据
* @param array $where
* @return array
*/
public function getTimetables(array $where = [])
{
// 获取日期范围,默认为本周
$start_date = $where['start_date'] ?? date('Y-m-d', strtotime('monday this week'));
$end_date = $where['end_date'] ?? date('Y-m-d', strtotime('sunday this week'));
// 校区ID
$campus_id = isset($where['campus_id']) && !empty($where['campus_id']) ? intval($where['campus_id']) : 0;
// 查询条件
$query_condition = [
['course_date', '>=', $start_date],
['course_date', '<=', $end_date]
];
// 如果指定了校区,添加校区筛选条件
if ($campus_id > 0) {
$query_condition[] = ['campus_id', '=', $campus_id];
}
// 查询指定日期范围内的课程安排
$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');
$person_schedules = [];
if (!empty($schedule_ids)) {
$person_schedules = (new PersonCourseSchedule())->where([
['schedule_id', 'in', $schedule_ids]
])->select()->toArray();
}
// 组织数据结构
$days = [];
$weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
// 获取所有时间段和教室
$time_slots = [];
$classrooms = [];
// 如果没有数据,设置默认的时间段和教室
if (empty($schedules)) {
$time_slots = ['9:00-10:00', '10:00-11:00', '11:00-12:00', '14:00-15:00', '15:00-16:00', '16:00-17:00'];
$classrooms = [1, 2]; // 默认教室ID
} else {
foreach ($schedules as $schedule) {
if (!in_array($schedule['time_slot'], $time_slots)) {
$time_slots[] = $schedule['time_slot'];
}
// 假设venue_id代表教室
if (!in_array($schedule['venue_id'], $classrooms)) {
$classrooms[] = $schedule['venue_id'];
}
}
}
// 按日期组织数据
$current_date = $start_date;
$day_index = (int)date('N', strtotime($current_date)) - 1; // 获取周几(1-7),转为索引(0-6)
while (strtotime($current_date) <= strtotime($end_date)) {
$day_schedules = array_filter($schedules, function($schedule) use ($current_date) {
return $schedule['course_date'] == $current_date;
});
$day_classrooms = empty($day_schedules) ? $classrooms : [];
if (!empty($day_schedules)) {
foreach ($day_schedules as $schedule) {
if (!in_array($schedule['venue_id'], $day_classrooms)) {
$day_classrooms[] = $schedule['venue_id'];
}
}
}
// 构建每个时间段的数据
$day_time_slots = [];
foreach ($time_slots as $time_slot) {
$slot_data = [
'timeRange' => $time_slot,
'color' => '#' . dechex(rand(0x000000, 0xFFFFFF)), // 随机颜色
];
// 查找该时间段的课程
$slot_schedule = array_filter($day_schedules, function($schedule) use ($time_slot) {
return $schedule['time_slot'] == $time_slot;
});
if (!empty($slot_schedule)) {
$schedule = reset($slot_schedule);
// 查找该课程的学员
$students = array_filter($person_schedules, function($person) use ($schedule) {
return $person['schedule_id'] == $schedule['id'];
});
$student_names = array_column($students, 'person_id');
$slot_data['course'] = [
'teacher' => $schedule['coach_id'] ?? '',
'students' => $student_names,
'classroom' => $schedule['venue_id'],
'hasnumber' => $schedule['available_capacity'] ?? 0,
];
}
$day_time_slots[] = $slot_data;
}
$days[] = [
'date' => $weekdays[$day_index % 7] . ' (' . $current_date . ')',
'timeSlots' => $day_time_slots,
'classrooms' => $day_classrooms,
];
$current_date = date('Y-m-d', strtotime($current_date . ' +1 day'));
$day_index = ($day_index + 1) % 7;
}
return $days;
}
/**
* 自动创建课程安排记录
* @param string $start_date 开始日期
* @param string $end_date 结束日期
* @param int $campus_id 校区ID
* @return array 创建的课程安排记录
*/
private function autoCreateSchedules(string $start_date, string $end_date, int $campus_id): array
{
$schedules = [];
$default_time_slots = ['9:00-10:00', '10:00-11:00', '11:00-12:00', '14:00-15:00', '15:00-16:00', '16:00-17:00'];
$default_venues = [1, 2]; // 默认教室ID
$current_date = $start_date;
// 遍历日期范围
while (strtotime($current_date) <= strtotime($end_date)) {
foreach ($default_venues as $venue_id) {
foreach ($default_time_slots as $time_slot) {
// 创建课程安排记录
$schedule_data = [
'campus_id' => $campus_id,
'venue_id' => $venue_id,
'course_date' => $current_date,
'time_slot' => $time_slot,
'course_id' => 0, // 默认课程ID
'coach_id' => 0, // 默认教练ID
'participants' => json_encode([]), // 空参与者列表
'student_ids' => json_encode([]), // 空学生列表
'available_capacity' => 10, // 默认容量
'status' => 'pending', // 默认状态
'created_by' => 'system', // 系统创建
];
// 插入数据库
$res = $this->model->create($schedule_data);
// 添加到返回结果
$schedule_data['id'] = $res->id;
$schedules[] = $schedule_data;
}
}
$current_date = date('Y-m-d', strtotime($current_date . ' +1 day'));
}
return $schedules;
}
}

Loading…
Cancel
Save