课程日历

dev
lion 4 months ago
parent 6bbb8abb47
commit 5c15941057

@ -54,14 +54,17 @@
</div> </div>
</div> </div>
</template> </template>
<template v-if="form.type===4" v-slot:url> <template v-if="form.type===4 || form.type===1" v-slot:url>
<div class="xy-table-item"> <div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold"> <div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>资讯链接 <span style="color: red;font-weight: bold;padding-right: 4px;"></span>资讯链接
</div> </div>
<div class="xy-table-item-content"> <div class="xy-table-item-content">
<el-input v-model="form.url" placeholder="请输入跳转链接,例:/news/2025-07-02/146.html" clearable <el-select filterable remote v-model="form.url" :remote-method="remoteMethod" :loading="loading"
style="width: 100%;"></el-input> placeholder="请输入关键词查询资讯" clearable style="width: 100%;">
<el-option v-for="item in zixunList" :key="item.id" :label="item.title" :value="item.titleurl">
</el-option>
</el-select>
</div> </div>
</div> </div>
</template> </template>
@ -83,10 +86,8 @@
<span style="color: red;font-weight: bold;padding-right: 4px;">*</span>开始时间 <span style="color: red;font-weight: bold;padding-right: 4px;">*</span>开始时间
</div> </div>
<div class="xy-table-item-content"> <div class="xy-table-item-content">
<el-date-picker v-model="form.start_time" <el-date-picker v-model="form.start_time" style="width: 100%;" value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100%;" format="yyyy-MM-dd HH:mm:ss" type="datetime" placeholder="选择日期时间" align="right"
value-format="yyyy-MM-dd HH:mm:ss" format="yyyy-MM-dd HH:mm:ss"
type="datetime" placeholder="选择日期时间" align="right"
:picker-options="pickerOptions"> :picker-options="pickerOptions">
</el-date-picker> </el-date-picker>
</div> </div>
@ -98,16 +99,14 @@
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>截止时间 <span style="color: red;font-weight: bold;padding-right: 4px;"></span>截止时间
</div> </div>
<div class="xy-table-item-content"> <div class="xy-table-item-content">
<el-date-picker v-model="form.end_time" <el-date-picker v-model="form.end_time" style="width: 100%;" value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100%;" format="yyyy-MM-dd HH:mm:ss" type="datetime" placeholder="选择日期时间" align="right"
value-format="yyyy-MM-dd HH:mm:ss" format="yyyy-MM-dd HH:mm:ss"
type="datetime" placeholder="选择日期时间" align="right"
:picker-options="pickerOptions"> :picker-options="pickerOptions">
</el-date-picker> </el-date-picker>
</div> </div>
</div> </div>
</template> </template>
<template v-slot:content v-if="form.type===3 || form.type===4"> <template v-slot:content v-if="form.type===3">
<div class="xy-table-item"> <div class="xy-table-item">
<div class="xy-table-item-label" style="font-weight: bold"> <div class="xy-table-item-label" style="font-weight: bold">
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>内容 <span style="color: red;font-weight: bold;padding-right: 4px;"></span>内容
@ -179,8 +178,9 @@
}, { }, {
id: 4, id: 4,
value: '资讯' value: '资讯'
}], }
course_content_options:[], ],
course_content_options: [],
course_options: [], course_options: [],
canSelect: false, canSelect: false,
form: { form: {
@ -203,17 +203,52 @@
required: true, required: true,
message: '请选择开始时间' message: '请选择开始时间'
}], }],
end_time: [{ // end_time: [{
required: true, // required: true,
message: '请选择结束时间' // message: ''
}], // }],
} },
zixunList:[],
} }
}, },
created() { created() {
this.getCourseList() this.getCourseList()
this.getZxList()
}, },
methods: { methods: {
remoteMethod(query) {
if (query !== '') {
this.loading = true;
this.getZxList(query)
} else {
this.zixunList = [];
}
},
async getZxList(query) {
const proUrl = `${process.env.VUE_APP_PRO_API}`
var baseUrl = proUrl + '/e/extend/news.php'
try {
// axios.get()
const response = await axios.get(baseUrl, {
params: {
page: 1,
pagesize: 999,
keyword: query
}, // GET URL
timeout: 5000 //
});
//
if (response.status === 200) {
this.zixunList = response.data.rows;
}
this.loading = false
console.log('GET 请求成功:', response);
} catch (error) {
//
console.error('GET 请求失败:', error);
alert('请求失败:' + (error.response?.data?.msg || '网络错误'));
}
},
async getCourseList() { async getCourseList() {
const res = await courseIndex({ const res = await courseIndex({
page: 1, page: 1,
@ -255,22 +290,23 @@
this.getCourseContentList(e) this.getCourseContentList(e)
} }
// //
if(this.form.type==1){ if (this.form.type == 1) {
this.course_options.map(item=>{ this.course_options.map(item => {
if(item.id===e){ if (item.id === e) {
this.form.title = item.name this.form.title = item.name
this.form.start_time = item.start_date this.form.start_time = item.start_date?item.start_date+' 00:00:00':''
this.form.end_time = item.end_date this.form.end_time = item.end_date?item.end_date+' 00:00:00':''
this.form.date = item.start_date this.form.date = item.start_date
this.form.url = item.url
} }
}) })
} }
}, },
changeCourseContent(e) { changeCourseContent(e) {
// //
if(this.form.type==2){ if (this.form.type == 2) {
this.course_content_options.map(item=>{ this.course_content_options.map(item => {
if(item.id===e){ if (item.id === e) {
this.form.title = item.theme this.form.title = item.theme
this.form.start_time = item.start_date this.form.start_time = item.start_date
this.form.end_time = item.end_date this.form.end_time = item.end_date
@ -311,9 +347,9 @@
isShow(newVal) { isShow(newVal) {
if (newVal) { if (newVal) {
if (this.type === 'editor') { if (this.type === 'editor') {
console.log("id",this.id) console.log("id", this.id)
this.getDetail() this.getDetail()
}else{ } else {
this.showTinymce = true this.showTinymce = true
} }
} else { } else {

@ -8,7 +8,7 @@
<div class="admin-main"> <div class="admin-main">
<div class="calendar-panel"> <div class="calendar-panel">
<div class="calendar-wrapper"> <div class="calendar-wrapper">
<el-calendar v-model="calendarDate" :first-day-of-week="7"> <el-calendar v-model="calendarDate" :first-day-of-week="1">
<template slot="dateCell" slot-scope="{date}"> <template slot="dateCell" slot-scope="{date}">
<div class="cell-content"> <div class="cell-content">
<span class="date-number">{{ date.getDate() }}</span> <span class="date-number">{{ date.getDate() }}</span>
@ -30,10 +30,10 @@
<div class="continuous-events-overlay"> <div class="continuous-events-overlay">
<div <div
v-for="event in getContinuousEvents()" v-for="event in getContinuousEvents()"
:key="`continuous-${event.id}-${event.weekStart}`" :key="`continuous-${event.id}-${event.segStartISO}`"
:class="['continuous-event', `event-type-${event.type || 'default'}`]" :class="['continuous-event', `event-type-${event.type || 'default'}`]"
:style="getContinuousEventStyle(event)" :style="getContinuousEventStyle(event)"
:title="getEventTooltip(event, new Date(event.weekStart))" :title="getEventTooltip(event, new Date(event.segStartISO))"
@click.stop="openCreateModal('editor', event.id)" @click.stop="openCreateModal('editor', event.id)"
> >
{{ event.title }} {{ event.title }}
@ -180,7 +180,8 @@ import addCalendar from './components/addCalendar.vue'
} }
}, },
getContinuousEvents() { getContinuousEvents() {
const FIRST_DOW = 0 // 0=Sunday to match :first-day-of-week="7" on el-calendar const FIRST_DOW = 1 // 1=Monday to match :first-day-of-week="1"
const OFFSET_DAYS = 0 //
const continuousEvents = [] const continuousEvents = []
// //
@ -204,9 +205,19 @@ import addCalendar from './components/addCalendar.vue'
return d return d
} }
function adjustEndForDisplay(d) {
// 00:00:00
const end = new Date(d)
if (end.getHours() === 0 && end.getMinutes() === 0 && end.getSeconds() === 0) {
end.setSeconds(end.getSeconds() - 1)
}
return end
}
multiDayEvents.forEach(ev => { multiDayEvents.forEach(ev => {
const eventStart = this.parseDateTime(ev.start_time) const eventStart = this.parseDateTime(ev.start_time)
const eventEnd = this.parseDateTime(ev.end_time) const eventEndRaw = this.parseDateTime(ev.end_time)
const eventEnd = adjustEndForDisplay(eventEndRaw)
if (eventEnd < monthStart || eventStart > monthEnd) return if (eventEnd < monthStart || eventStart > monthEnd) return
@ -227,6 +238,14 @@ import addCalendar from './components/addCalendar.vue'
const startCol = (segStart.getDay() - FIRST_DOW + 7) % 7 const startCol = (segStart.getDay() - FIRST_DOW + 7) % 7
const endCol = (segEnd.getDay() - FIRST_DOW + 7) % 7 const endCol = (segEnd.getDay() - FIRST_DOW + 7) % 7
const spanCols = endCol - startCol + 1 const spanCols = endCol - startCol + 1
//
const adjStart = new Date(segStart)
adjStart.setDate(adjStart.getDate() + OFFSET_DAYS)
const displayWeekStart = getWeekStart(adjStart)
const displayStartCol = (adjStart.getDay() - FIRST_DOW + 7) % 7
const displayEndCol = displayStartCol + spanCols - 1
continuousEvents.push({ continuousEvents.push({
...ev, ...ev,
weekStartISO: weekStart.toISOString(), weekStartISO: weekStart.toISOString(),
@ -234,7 +253,13 @@ import addCalendar from './components/addCalendar.vue'
segEndISO: segEnd.toISOString(), segEndISO: segEnd.toISOString(),
startCol, startCol,
spanCols, spanCols,
endCol endCol,
//
adjSegStartISO: adjStart.toISOString(),
displayWeekStartISO: displayWeekStart.toISOString(),
displayStartCol,
displayEndCol,
laneIndex: 0
}) })
} }
cursor.setDate(cursor.getDate() + 7) cursor.setDate(cursor.getDate() + 7)
@ -244,28 +269,42 @@ import addCalendar from './components/addCalendar.vue'
// lane // lane
const byWeek = {} const byWeek = {}
continuousEvents.forEach(seg => { continuousEvents.forEach(seg => {
const key = seg.weekStartISO const key = seg.displayWeekStartISO || seg.weekStartISO
if (!byWeek[key]) byWeek[key] = [] if (!byWeek[key]) byWeek[key] = []
byWeek[key].push(seg) byWeek[key].push(seg)
}) })
Object.values(byWeek).forEach(segs => { Object.values(byWeek).forEach(segs => {
// // id
segs.sort((a, b) => (a.startCol - b.startCol) || (b.spanCols - a.spanCols)) segs.sort((a, b) => (a.displayStartCol - b.displayStartCol) || (b.spanCols - a.spanCols) || String(a.id).localeCompare(String(b.id)))
const laneEndCols = [] // const laneEndCols = [] //
const placedSegs = []
segs.forEach(seg => { segs.forEach(seg => {
let placed = false let placed = false
for (let i = 0; i < laneEndCols.length; i += 1) { for (let i = 0; i < laneEndCols.length; i += 1) {
if (laneEndCols[i] < seg.startCol) { // <
if (laneEndCols[i] < seg.displayStartCol) {
seg.laneIndex = i seg.laneIndex = i
laneEndCols[i] = seg.endCol laneEndCols[i] = seg.displayEndCol
placed = true placed = true
break break
} }
} }
if (!placed) { if (!placed) {
seg.laneIndex = laneEndCols.length seg.laneIndex = laneEndCols.length
laneEndCols.push(seg.endCol) laneEndCols.push(seg.displayEndCol)
}
placedSegs.push(seg)
})
//
const seen = {}
placedSegs.forEach(seg => {
const k = `${seg.displayStartCol}-${seg.displayEndCol}-${seg.laneIndex}`
if (seen[k]) {
//
seg.laneIndex = ++seen[k].maxLane
} else {
seen[k] = { maxLane: seg.laneIndex }
} }
}) })
}) })
@ -273,23 +312,26 @@ import addCalendar from './components/addCalendar.vue'
return continuousEvents return continuousEvents
}, },
getContinuousEventStyle(event) { getContinuousEventStyle(event) {
const FIRST_DOW = 0 const FIRST_DOW = 1
const currentMonth = this.calendarDate.getMonth() const currentMonth = this.calendarDate.getMonth()
const currentYear = this.calendarDate.getFullYear() const currentYear = this.calendarDate.getFullYear()
const firstDay = new Date(currentYear, currentMonth, 1) const firstDay = new Date(currentYear, currentMonth, 1)
// 7 // 使
const adjStart = new Date(event.segStartISO) const adjStart = new Date(event.adjSegStartISO || event.segStartISO)
adjStart.setDate(adjStart.getDate() + 7)
const msPerDay = 1000 * 60 * 60 * 24 const msPerDay = 1000 * 60 * 60 * 24
const daysFromFirstOfMonth = Math.floor((adjStart - firstDay) / msPerDay)
const firstDayOffset = (firstDay.getDay() - FIRST_DOW + 7) % 7 const firstDayOffset = (firstDay.getDay() - FIRST_DOW + 7) % 7
// 使
const displayWeekStart = new Date(event.displayWeekStartISO || adjStart)
const daysFromFirstOfMonth = Math.floor((displayWeekStart - firstDay) / msPerDay)
const totalDaysFromCalendarStart = daysFromFirstOfMonth + firstDayOffset const totalDaysFromCalendarStart = daysFromFirstOfMonth + firstDayOffset
const weekRow = Math.floor(totalDaysFromCalendarStart / 7) const weekRow = Math.floor(totalDaysFromCalendarStart / 7)
// 使 // 使
const startColAdjusted = (adjStart.getDay() - FIRST_DOW + 7) % 7 const startColAdjusted = (event.displayStartCol != null)
? event.displayStartCol
: (adjStart.getDay() - FIRST_DOW + 7) % 7
const cellWidth = 100 / 7 const cellWidth = 100 / 7
const cellHeight = 100 const cellHeight = 100

@ -57,8 +57,9 @@
<span style="color: red;font-weight: bold;padding-right: 4px;"></span>资讯链接 <span style="color: red;font-weight: bold;padding-right: 4px;"></span>资讯链接
</div> </div>
<div class="xy-table-item-content"> <div class="xy-table-item-content">
<el-select v-model="form.url" placeholder="请选择" clearable style="width: 100%;"> <el-select filterable remote v-model="form.url" :remote-method="remoteMethod" :loading="loading"
<el-option v-for="item in false_or_true" :key="item.id" :label="item.value" :value="item.id"> placeholder="请输入关键词查询资讯" clearable style="width: 100%;">
<el-option v-for="item in zixunList" :key="item.id" :label="item.title" :value="item.titleurl">
</el-option> </el-option>
</el-select> </el-select>
</div> </div>
@ -406,30 +407,41 @@
sort_type: 'ASC' sort_type: 'ASC'
}, },
formList: [], formList: [],
zxkeyword:'', loading: false,
zixunList:[] zixunList: []
} }
}, },
created() { created() {
this.getZxList() this.getZxList()
}, },
methods: { methods: {
async getZxList() { remoteMethod(query) {
if (query !== '') {
this.loading = true;
this.getZxList(query)
} else {
this.zixunList = [];
}
},
async getZxList(query) {
const proUrl = `${process.env.VUE_APP_PRO_API}` const proUrl = `${process.env.VUE_APP_PRO_API}`
var baseUrl = proUrl + '/e/extend/news.php' var baseUrl = proUrl + '/e/extend/news.php'
try { try {
// axios.get() // axios.get()
const response = await axios.get(baseUrl, { const response = await axios.get(baseUrl, {
params: { params: {
page:1, page: 1,
pagesize:5, pagesize: 999,
keyword:this.zxkeyword keyword: query
}, // GET URL }, // GET URL
timeout: 5000 // timeout: 5000 //
}); });
// //
this.zixunList = response.data; if(response.status===200){
console.log('GET 请求成功:', response.data); this.zixunList = response.data.rows;
}
this.loading = false
console.log('GET 请求成功:', response);
} catch (error) { } catch (error) {
// //
console.error('GET 请求失败:', error); console.error('GET 请求失败:', error);

@ -31,7 +31,7 @@
<div class="course-info"> <div class="course-info">
<span><i class="el-icon-date"></i> {{ course.date }} - {{course.period}}</span> <span><i class="el-icon-date"></i> {{ course.date }} - {{course.period}}</span>
<span><i class="el-icon-location"></i> {{ course.address }}</span> <span><i class="el-icon-location"></i> {{ course.address }}</span>
<span><i class="el-icon-user"></i> {{ course.teacher.name }}</span> <span><i class="el-icon-user"></i> {{ course.teacher?course.teacher.name:'' }}</span>
</div> </div>
</div> </div>
</template> </template>
@ -64,7 +64,7 @@
<div><strong>主题</strong>{{ selectedCourse.theme }}</div> <div><strong>主题</strong>{{ selectedCourse.theme }}</div>
<div><strong>时间</strong>{{ selectedCourse.date }} - {{selectedCourse.period}}</div> <div><strong>时间</strong>{{ selectedCourse.date }} - {{selectedCourse.period}}</div>
<div><strong>地点</strong>{{ selectedCourse.address }}</div> <div><strong>地点</strong>{{ selectedCourse.address }}</div>
<div><strong>主讲</strong>{{ selectedCourse.teacher.name }}</div> <div><strong>主讲</strong>{{ selectedCourse.teacher?selectedCourse.teacher.name:'' }}</div>
</div> </div>
</div> </div>
<div class="qr-actions"> <div class="qr-actions">
@ -186,7 +186,13 @@
value: date ? date : '' value: date ? date : ''
}] }]
}) })
if(res.data.length>0){
this.courseContentList = res.data this.courseContentList = res.data
}else{
this.courseContentList = []
this.$message.warning("该课程还未添加课表");
}
}, },
getCousreContent(){ getCousreContent(){
this.getCourseContent(this.course_id,this.course_date) this.getCourseContent(this.course_id,this.course_date)

Loading…
Cancel
Save