Browse Source

Merge branch 'wangzeyan'

master
王泽彦 11 months ago
parent
commit
a068ac8499
  1. 4
      admin/src/app/lang/zh-cn/course.course.json
  2. 22
      admin/src/app/views/course/components/course-edit.vue
  3. 13
      admin/src/app/views/course/course.vue
  4. 269
      admin/src/app/views/timetables/timetables.vue
  5. 1
      niucloud/app/adminapi/controller/course/Course.php
  6. 108
      niucloud/app/model/course/Course.php

4
admin/src/app/lang/zh-cn/course.course.json

@ -9,8 +9,8 @@
"durationPlaceholder": "请输入课程时长",
"sessionCount": "课时数量",
"sessionCountPlaceholder": "请输入课时数量",
"singleSessionCount": "单次逍客数量",
"singleSessionCountPlaceholder": "请输入单次逍客数量",
"singleSessionCount": "单次消课数量",
"singleSessionCountPlaceholder": "请输入单次消课数量",
"price": "课程价格",
"pricePlaceholder": "请输入课程价格",
"internalReminder": "内部提醒课时",

22
admin/src/app/views/course/components/course-edit.vue

@ -24,14 +24,19 @@
</el-form-item>
<el-form-item :label="t('courseType')" prop="course_type">
<el-input
<el-select
class="input-width"
v-model="formData.course_type"
clearable
:placeholder="t('courseTypePlaceholder')"
class="input-width"
/>
>
<el-option
v-for="(item, index) in courseTypeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('duration')" prop="duration">
<el-input
v-model="formData.duration"
@ -193,6 +198,13 @@ const formRules = computed(() => {
const emit = defineEmits(['complete'])
const courseTypeList = ref([])
const getcourseTypeList = async () => {
courseTypeList.value = await (
await useDictionary('course_type')
).data.dictionary
}
getcourseTypeList()
/**
* 确认
* @param formEl

13
admin/src/app/views/course/course.vue

@ -24,10 +24,17 @@
/>
</el-form-item>
<el-form-item :label="t('courseType')" prop="course_type">
<el-input
<el-select
v-model="courseTable.searchParam.course_type"
:placeholder="t('courseTypePlaceholder')"
/>
>
<el-option
v-for="item in courseTypeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('duration')" prop="duration">
<el-input
@ -220,7 +227,7 @@ let courseTable = reactive({
remarks: '',
},
})
const courseTypeList = useDictionary('course_type')
const searchFormRef = ref<FormInstance>()
//

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

@ -1,125 +1,198 @@
<template>
<div class="schedule-container">
<!-- 周视图日历 -->
<FullCalendar
:options="calendarOptions"
ref="fullCalendar"
class="calendar"
/>
<!-- 课程详情弹窗 -->
<el-dialog
v-model="dialogVisible"
title="课程人员安排"
width="30%"
:before-close="handleClose"
>
<div v-if="selectedEvent">
<h3>{{ selectedEvent.title }}</h3>
<p>时间{{ selectedEvent.time }}</p>
<h4>参与人员</h4>
<ul>
<li v-for="person in selectedEvent.people" :key="person.id">
{{ person.name }} - {{ person.role }}
</li>
</ul>
<el-card class="box-card !border-none" shadow="never">
<el-button @click="addDict">添加</el-button>
<div class="schedule-container">
<!-- 周一到周日的布局 -->
<div v-for="(day, index) in days" :key="index" class="day-column">
<div class="day-header">{{ day.date }}</div>
<el-table
:data="day.timeSlots"
border
:span-method="(data) => objectSpanMethod(day.timeSlots, data)"
style="width: 100%"
@cell-click="handleCellClick"
>
<!-- 时间列 -->
<el-table-column
prop="timeRange"
label="时间"
width="80"
align="center"
>
<template #default="{ row }">
<div :style="{ backgroundColor: row.color }">
{{ row.timeRange }}
</div>
</template>
</el-table-column>
<!-- 教室列 -->
<el-table-column
v-for="(classroom, idx) in day.classrooms"
:key="idx"
:label="`教室${idx + 1}`"
:prop="`classroom${idx + 1}`"
align="center"
>
<template #default="{ row }">
<div v-if="row.course && row.course.classroom === classroom">
<div class="teacher-name">{{ row.course.teacher }}</div>
<div class="student-list">
<el-tag
v-for="student in row.course.students"
:key="student"
size="small"
effect="plain"
>
{{ student }}
</el-tag>
</div>
<div class="classroom-name">
剩余空位{{ row.course.hasnumber }}
</div>
</div>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</div>
<!-- 详情弹窗 -->
<el-dialog v-model="dialogVisible" title="课程详情">
<p><strong>教师:</strong> {{ selectedCourse?.teacher }}</p>
<p><strong>学员:</strong> {{ selectedCourse?.students.join(', ') }}</p>
</el-dialog>
</div>
</el-card>
</template>
<script setup>
import { ref } from 'vue'
import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction' //
import zhLocale from '@fullcalendar/core/locales/zh-cn'
//
const courses = ref([
const days = [
//
{
id: 1,
title: '数学课',
start: '2025-05-18 08:00:00',
end: '2025-05-18 09:00:00',
people: [
{ id: 1, name: '张老师', role: '主讲' },
{ id: 2, name: '李同学', role: '学生' },
date: '周一',
timeSlots: [
{
timeRange: '9:00-10:00',
color: '#FFA07A',
course: {
teacher: '',
students: [],
classroom: '教室1',
hasnumber: 5,
},
},
// ...
],
classrooms: ['教室1'], //
},
{
id: 2,
title: '英语课',
start: '2025-05-18 10:00:00',
end: '2025-05-18 11:00:00',
people: [{ id: 3, name: '王老师', role: '主讲' }],
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'], //
},
])
]
//
const dialogVisible = ref(false)
const selectedEvent = ref(null)
// FullCalendar
const calendarOptions = ref({
locale: zhLocale,
plugins: [
dayGridPlugin,
timeGridPlugin,
interactionPlugin, //
],
initialView: 'timeGridWeek', //
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'timeGridWeek,timeGridDay', //
},
events: courses.value,
eventClick: (info) => {
selectedEvent.value = {
title: info.event.title,
time: `${info.event.start.toLocaleTimeString()} - ${info.event.end.toLocaleTimeString()}`,
people: info.event.extendedProps.people,
const selectedCourse = ref(null)
//
const objectSpanMethod = (timeSlots, { row, column, rowIndex }) => {
if (column.property === 'timeRange') {
//
const current = timeSlots[rowIndex]
//
if (!current || !current.timeRange) return { rowspan: 0, colspan: 0 }
let spanCount = 1
//
while (timeSlots[rowIndex + spanCount]?.timeRange === current.timeRange) {
spanCount++
}
dialogVisible.value = true
},
eventContent: (arg) => {
//
return { html: `<div class="fc-event-title">${arg.event.title}</div>` }
},
allDaySlot: false, //
slotDuration: '01:00', // 1
slotLabelFormat: {
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: false,
meridiem: 'short',
},
})
//
const handleClose = () => {
dialogVisible.value = false
selectedEvent.value = null
//
if (
spanCount > 1 &&
rowIndex > 0 &&
timeSlots[rowIndex - 1]?.timeRange === current.timeRange
) {
return { rowspan: 0, colspan: 0 }
}
return { rowspan: spanCount, colspan: 1 }
}
return { rowspan: 1, colspan: 1 }
}
//
const handleCellClick = (row, column, cell, event) => {
console.log(row, column, cell, event)
if (column.property.startsWith('classroom')) {
selectedCourse.value = row.course
dialogVisible.value = true
}
}
</script>
<style>
<style scoped>
.schedule-container {
padding: 20px;
display: flex;
gap: 10px;
}
.day-column {
flex: 1;
min-width: 200px;
}
.day-header {
text-align: center;
font-weight: bold;
padding: 8px;
background-color: #f0f0f0;
}
.teacher-name {
font-weight: bold;
margin-bottom: 5px;
}
.calendar {
margin-bottom: 20px;
.student-list {
margin-top: 5px;
}
.fc-event-title {
white-space: normal !important;
font-size: 14px;
.el-table__cell {
display: flex;
align-items: center;
}
.fc-timegrid-event {
cursor: pointer;
.classroom-name {
margin-bottom: 5px;
}
</style>

1
niucloud/app/adminapi/controller/course/Course.php

@ -105,5 +105,4 @@ class Course extends BaseAdminController
return success('DELETE_SUCCESS');
}
}

108
niucloud/app/model/course/Course.php

@ -17,9 +17,9 @@ use think\model\relation\HasMany;
use think\model\relation\HasOne;
/**
* 校区模型
* Class Campus
* @package app\model\campus
* 课程模型
* Class Course
* @package app\model\course
*/
class Course extends BaseModel
{
@ -51,38 +51,122 @@ class Course extends BaseModel
protected $defaultSoftDelete = 0;
/**
* 搜索器:校区校区名称
* 搜索器:课程课程编号
* @param $value
* @param $data
*/
public function searchCampusNameAttr($query, $value, $data)
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_name", "like", "%".$value."%");
$query->where("id", $value);
}
}
/**
* 搜索器:校区校区地址
* 搜索器:课程课程名称
* @param $value
* @param $data
*/
public function searchCampusAddressAttr($query, $value, $data)
public function searchCourseNameAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_address", $value);
$query->where("course_name", $value);
}
}
/**
* 搜索器:校区校区状态
* 搜索器:课程课程类型
* @param $value
* @param $data
*/
public function searchCampusStatusAttr($query, $value, $data)
public function searchCourseTypeAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_status", $value);
$query->where("course_type", $value);
}
}
/**
* 搜索器:课程课程时长
* @param $value
* @param $data
*/
public function searchDurationAttr($query, $value, $data)
{
if ($value) {
$query->where("duration", $value);
}
}
/**
* 搜索器:课程课时数量
* @param $value
* @param $data
*/
public function searchSessionCountAttr($query, $value, $data)
{
if ($value) {
$query->where("session_count", $value);
}
}
/**
* 搜索器:课程单次逍客数量
* @param $value
* @param $data
*/
public function searchSingleSessionCountAttr($query, $value, $data)
{
if ($value) {
$query->where("single_session_count", $value);
}
}
/**
* 搜索器:课程课程价格
* @param $value
* @param $data
*/
public function searchPriceAttr($query, $value, $data)
{
if ($value) {
$query->where("price", $value);
}
}
/**
* 搜索器:课程内部提醒课时
* @param $value
* @param $data
*/
public function searchInternalReminderAttr($query, $value, $data)
{
if ($value) {
$query->where("internal_reminder", $value);
}
}
/**
* 搜索器:课程客户提醒课时
* @param $value
* @param $data
*/
public function searchCustomerReminderAttr($query, $value, $data)
{
if ($value) {
$query->where("customer_reminder", $value);
}
}
/**
* 搜索器:课程课程备注
* @param $value
* @param $data
*/
public function searchRemarksAttr($query, $value, $data)
{
if ($value) {
$query->where("remarks", $value);
}
}

Loading…
Cancel
Save