完善部分设备管理

This commit is contained in:
LiuQAQQWQ 2025-12-31 09:31:05 +08:00
parent 440117b417
commit 09f7175f82
13 changed files with 555 additions and 143 deletions

View File

@ -14,3 +14,9 @@ export function getWorkbench() {
export function getDictData(params: any) { export function getDictData(params: any) {
return request.get({ url: '/config/dict', params }) return request.get({ url: '/config/dict', params })
} }
// 真的字典数据
export function getDictList(params: any) {
return request.get({ url: '/setting.dict.dict_data/lists', params })
}

View File

@ -0,0 +1,26 @@
import request from '@/utils/request'
// 资产信息列表
export function infoLists(params?: Record<string, any>) {
return request.get({ url: '/info/list', params })
}
// 资产信息详情
export function infoDetail(params: Record<string, any>) {
return request.get({ url: '/info/detail', params })
}
// 资产信息新增
export function infoAdd(params: Record<string, any>) {
return request.post({ url: '/info/add', params })
}
// 资产信息编辑
export function infoEdit(params: Record<string, any>) {
return request.post({ url: '/info/edit', params })
}
// 资产信息删除
export function infoDelete(params: Record<string, any>) {
return request.post({ url: '/info/del', params })
}

View File

@ -37,5 +37,5 @@ export function taskCourseList(params: Record<string, any>) {
// 班级教学任务教师列表 // 班级教学任务教师列表
export function taskTeacherList(params: Record<string, any>) { export function taskTeacherList(params: Record<string, any>) {
return request.get({ url: '/task', params }) return request.get({ url: '/task/teacher', params })
} }

View File

@ -15,6 +15,11 @@ export function timeCurrentSemester() {
return request.get({ url: '/time/currentSemester' }) return request.get({ url: '/time/currentSemester' })
} }
// 获取下一学期信息
export function timeNextSemester() {
return request.get({ url: '/time/nextSemester' })
}
// 获取刷新配置 // 获取刷新配置
export function timeRefreshConfig() { export function timeRefreshConfig() {
return request.get({ url: '/time/refreshConfig' }) return request.get({ url: '/time/refreshConfig' })

View File

@ -1,6 +1,6 @@
import { reactive, toRaw } from 'vue' import { reactive, toRaw } from 'vue'
import { getDictData } from '@/api/app' import { getDictList } from '@/api/app'
interface Options { interface Options {
[propName: string]: { [propName: string]: {
@ -51,13 +51,13 @@ export function useDictOptions<T = any>(options: Options) {
// dict: any[] // dict: any[]
// }>(['dict']) // }>(['dict'])
export function useDictData<T = any>(dict: string) { export function useDictData<T = any>(id: number) {
const dictData: any = reactive({}) const dictData: any = reactive({})
const refresh = async () => { const refresh = async () => {
const data = await getDictData({ const data = await getDictList({
type: dict type_id: id
}) })
Object.assign(dictData, data) Object.assign(dictData, data.lists)
} }
refresh() refresh()

View File

@ -0,0 +1,168 @@
<template>
<div class="edit-popup">
<popup
ref="popupRef"
:title="popupTitle"
:async="true"
width="550px"
:clickModalClose="true"
@confirm="handleSubmit"
@close="handleClose"
>
<el-form ref="formRef" :model="formData" label-width="84px" :rules="formRules">
<el-form-item label="唯一标识" prop="id">
<el-input v-model="formData.id" placeholder="请输入唯一标识" />
</el-form-item>
<el-form-item label="资产名称" prop="assetName">
<el-input v-model="formData.assetName" placeholder="请输入资产名称" />
</el-form-item>
<el-form-item label="规格型号" prop="model">
<el-input v-model="formData.model" placeholder="请输入规格型号" />
</el-form-item>
<el-form-item label="品牌" prop="brand">
<el-input v-model="formData.brand" placeholder="请输入品牌" />
</el-form-item>
<el-form-item label="资产类别" prop="category">
<el-input v-model="formData.category" placeholder="请输入资产类别" />
</el-form-item>
<el-form-item label="是否为固定资产" prop="isFixedAsset">
<el-input v-model="formData.isFixedAsset" placeholder="请输入是否为固定资产" />
</el-form-item>
<el-form-item label="非固定资产库存信息ID" prop="consumableId">
<el-input v-model="formData.consumableId" placeholder="请输入非固定资产库存信息ID" />
</el-form-item>
<el-form-item label="资产描述" prop="description">
<el-input
v-model="formData.description"
placeholder="请输入资产描述"
type="textarea"
:autosize="{ minRows: 4, maxRows: 6 }"
/>
</el-form-item>
</el-form>
</popup>
</div>
</template>
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import { infoEdit, infoAdd, infoDetail } from '@/api/asset/info'
import Popup from '@/components/popup/index.vue'
import feedback from '@/utils/feedback'
import type { PropType } from 'vue'
defineProps({
dictData: {
type: Object as PropType<Record<string, any[]>>,
default: () => ({})
}
})
const emit = defineEmits(['success', 'close'])
const formRef = shallowRef<FormInstance>()
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
const popupTitle = computed(() => {
return mode.value == 'edit' ? '编辑资产信息' : '新增资产信息'
})
const formData = reactive({
id: '',
assetName: '',
model: '',
brand: '',
category: '',
isFixedAsset: '',
consumableId: '',
description: '',
})
const formRules = {
id: [
{
required: true,
message: '请输入唯一标识',
trigger: ['blur']
}
],
assetName: [
{
required: true,
message: '请输入资产名称',
trigger: ['blur']
}
],
model: [
{
required: true,
message: '请输入规格型号',
trigger: ['blur']
}
],
brand: [
{
required: true,
message: '请输入品牌',
trigger: ['blur']
}
],
category: [
{
required: true,
message: '请输入资产类别',
trigger: ['blur']
}
],
isFixedAsset: [
{
required: true,
message: '请输入是否为固定资产',
trigger: ['blur']
}
],
consumableId: [
{
required: true,
message: '请输入非固定资产库存信息ID',
trigger: ['blur']
}
],
}
const handleSubmit = async () => {
await formRef.value?.validate()
const data: any = { ...formData }
mode.value == 'edit' ? await infoEdit(data) : await infoAdd(data)
popupRef.value?.close()
feedback.msgSuccess('操作成功')
emit('success')
}
const open = (type = 'add') => {
mode.value = type
popupRef.value?.open()
}
const setFormData = async (data: Record<string, any>) => {
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
formData[key] = data[key]
}
}
}
const getDetail = async (row: Record<string, any>) => {
const data = await infoDetail({
id: row.id
})
setFormData(data)
}
const handleClose = () => {
emit('close')
}
defineExpose({
open,
setFormData,
getDetail
})
</script>

View File

@ -0,0 +1,151 @@
<template>
<div class="index-lists">
<el-card class="!border-none" shadow="never">
<el-form ref="formRef" class="mb-[-16px]" :model="queryParams" :inline="true">
<el-form-item label="资产名称" prop="assetName">
<el-input class="w-[280px]" v-model="queryParams.assetName" />
</el-form-item>
<el-form-item label="规格型号" prop="model">
<el-input class="w-[280px]" v-model="queryParams.model" />
</el-form-item>
<el-form-item label="品牌" prop="brand">
<el-input class="w-[280px]" v-model="queryParams.brand" />
</el-form-item>
<el-form-item label="资产类别" prop="category">
<el-select
v-model="queryParams.category"
class="w-[280px]"
clearable
>
<el-option label="全部" value="" />
<el-option
v-for="(item, index) in dictData.asset_classification"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="是否为固定资产" prop="isFixedAsset">
<el-input class="w-[280px]" v-model="queryParams.isFixedAsset" />
</el-form-item>
<el-form-item label="非固定资产库存信息ID" prop="consumableId">
<el-input class="w-[280px]" v-model="queryParams.consumableId" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="!border-none mt-4" shadow="never">
<div>
<el-button v-perms="['info:add']" type="primary" @click="handleAdd()">
<template #icon>
<icon name="el-icon-Plus" />
</template>
新增
</el-button>
</div>
<el-table
class="mt-4"
size="large"
v-loading="pager.loading"
:data="pager.lists"
>
<el-table-column label="资产名称" prop="assetName" min-width="100" />
<el-table-column label="规格型号" prop="model" min-width="100" />
<el-table-column label="品牌" prop="brand" min-width="100" />
<el-table-column label="资产类别" prop="category" min-width="100">
<template #default="{ row }">
<dict-value :options="dictData.asset_classification" :value="row.category" />
</template>
</el-table-column>
<el-table-column label="是否为固定资产" prop="isFixedAsset" min-width="100" />
<el-table-column label="非固定资产库存信息ID" prop="consumableId" min-width="100" />
<el-table-column label="资产描述" prop="description" min-width="100" />
<el-table-column label="记录创建时间" prop="createdTime" min-width="100" />
<el-table-column label="最后更新时间" prop="updatedTime" min-width="100" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button
v-perms="['info:edit']"
type="primary"
link
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
v-perms="['info:del']"
type="danger"
link
@click="handleDelete(row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="flex justify-end mt-4">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<edit-popup
v-if="showEdit"
ref="editRef"
:dict-data="dictData"
@success="getLists"
@close="showEdit = false"
/>
</div>
</template>
<script lang="ts" setup name="info">
import { infoDelete, infoLists } from '@/api/asset/info'
import { useDictData } from '@/hooks/useDictOptions'
import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback'
import EditPopup from './edit.vue'
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
const showEdit = ref(false)
const queryParams = reactive({
assetName: '',
model: '',
brand: '',
category: '',
isFixedAsset: '',
consumableId: '',
})
const { pager, getLists, resetPage, resetParams } = usePaging({
fetchFun: infoLists,
params: queryParams
})
const { dictData } = useDictData<{
asset_classification: any[]
}>(7)
const handleAdd = async () => {
showEdit.value = true
await nextTick()
editRef.value?.open('add')
}
const handleEdit = async (data: any) => {
showEdit.value = true
await nextTick()
editRef.value?.open('edit')
editRef.value?.getDetail(data)
}
const handleDelete = async (id: number) => {
await feedback.confirm('确定要删除?')
await infoDelete({ id })
feedback.msgSuccess('删除成功')
getLists()
}
getLists()
</script>

View File

@ -182,9 +182,9 @@
<el-option <el-option
v-for="(item, index) in optionsData.dictType" v-for="(item, index) in optionsData.dictType"
:key="index" :key="index"
:label="item.dictName" :label="item.name"
:value="item.dictType" :value="item.type"
:disabled="!item.dictStatus" :disabled="!item.status"
/> />
</el-select> </el-select>
</template> </template>

View File

@ -93,11 +93,11 @@
<div class="index-info"> <div class="index-info">
<div class="index-details"> <div class="index-details">
<div class="flex justify-center" style="font-size: 12px"> <div class="flex justify-center" style="font-size: 12px">
{{ getRowStartTime(row) }} {{ row.startTime }}
</div> </div>
<div class="flex justify-center" style="font-size: 6px"></div> <div class="flex justify-center" style="font-size: 6px"></div>
<div class="flex justify-center" style="font-size: 12px"> <div class="flex justify-center" style="font-size: 12px">
{{ getRowEndTime(row) }} {{ row.endTime }}
</div> </div>
</div> </div>
</div> </div>
@ -111,26 +111,26 @@
min-width="80" min-width="80"
align="center" align="center"
> >
<template #default="{ row, $index }"> <template #default="{ row }">
<div <div
:class="[ :class="[
'time-slot-cell', 'time-slot-cell',
isTimeSlotSelected($index, day.value, row[day.value]) ? 'time-slot-selected' : '', isTimeSlotSelected(row.section, day.value) ? 'time-slot-selected' : '',
row[day.value] ? '' : 'empty-slot' getScheduledClass(row.section, day.value) ? '' : 'empty-slot'
]" ]"
@click="handleTimeSlotClick($index, day.value, row[day.value])" @click="handleTimeSlotClick(row, day.value)"
> >
<!-- 如果有课程信息显示课程 --> <!-- 如果有课程信息显示课程 -->
<div v-if="row[day.value]" class="course-info"> <div v-if="getScheduledClass(row.section, day.value)" class="course-info">
<div class="course-name">{{ row[day.value].courseName || '课程' }}</div> <div class="course-name">{{ getScheduledClass(row.section, day.value)?.courseName || '课程' }}</div>
<div class="course-details"> <div class="course-details">
<div>教师: {{ row[day.value].teacherName || '未知' }}</div> <div>教师: {{ getScheduledClass(row.section, day.value)?.teacherName || '未知' }}</div>
<div>教室: {{ row[day.value].classroomName || '未知' }}</div> <div>教室: {{ getScheduledClass(row.section, day.value)?.classroomName || '未知' }}</div>
</div> </div>
</div> </div>
<!-- 空时间段显示可选状态 --> <!-- 空时间段显示可选状态 -->
<div v-else class="empty-slot-content"> <div v-else class="empty-slot-content">
<el-icon v-if="isTimeSlotSelected($index, day.value, row[day.value])" class="check-icon"> <el-icon v-if="isTimeSlotSelected(row.section, day.value)" class="check-icon">
<Check /> <Check />
</el-icon> </el-icon>
<span v-else class="empty-text">点击选择</span> <span v-else class="empty-text">点击选择</span>
@ -157,9 +157,9 @@
<div v-else class="space-y-4 max-h-[700px] overflow-y-auto"> <div v-else class="space-y-4 max-h-[700px] overflow-y-auto">
<el-card <el-card
v-for="item in getCourseLists.lists" v-for="item in getCourseLists.lists"
:key="`course-${item.id}`" :key="`course-${item.courseId}`"
shadow="hover" shadow="hover"
:class="['cursor-pointer transition-all duration-200', selectedCourseId === item.id ? 'selected-card' : 'hover:bg-blue-50']" :class="['cursor-pointer transition-all duration-200', selectedCourseId === item.courseId ? 'selected-card' : 'hover:bg-blue-50']"
@click="handleCourseClick(item)" @click="handleCourseClick(item)"
> >
<div class="p-0"> <div class="p-0">
@ -169,7 +169,7 @@
课程{{ item.courseName }} 课程{{ item.courseName }}
</h4> </h4>
</div> </div>
<el-tag v-if="selectedCourseId === item.id" size="small" type="info"> <el-tag v-if="selectedCourseId === item.courseId" size="small" type="info">
{{ item.totalWeeks }} {{ item.totalWeeks }}
</el-tag> </el-tag>
</div> </div>
@ -178,7 +178,7 @@
<span>总学时: {{ item.totalHours }}</span> <span>总学时: {{ item.totalHours }}</span>
</div> </div>
<div <div
v-if="selectedCourseId === item.id" v-if="selectedCourseId === item.courseId"
class="pt-3 border-t border-gray-100" class="pt-3 border-t border-gray-100"
> >
<div class="text-xs text-gray-700"> <div class="text-xs text-gray-700">
@ -310,9 +310,9 @@ import { Check } from '@element-plus/icons-vue'
import { classLists } from '@/api/class' import { classLists } from '@/api/class'
import { configLists } from '@/api/config' import { configLists } from '@/api/config'
import { courseAvailableRooms, courseAvailableSlots, courseSchedule } from '@/api/course' import { courseAvailableRooms, courseAvailableSlots, courseSchedule, courseScheduleList } from '@/api/course'
import { taskLists } from '@/api/task' import { taskLists } from '@/api/task'
import { timeCurrentSemester } from '@/api/time' import { timeNextSemester } from '@/api/time'
import { usePaging } from '@/hooks/usePaging' import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback' import feedback from '@/utils/feedback'
@ -347,7 +347,7 @@ interface ClassroomItem {
// //
interface SelectedTimeSlot { interface SelectedTimeSlot {
id: number id: number
rowIndex: number section: number
dayOfWeek: number dayOfWeek: number
startTime: string startTime: string
endTime: string endTime: string
@ -373,6 +373,34 @@ interface ClassItem {
maxStudentCount: number maxStudentCount: number
} }
//
interface ScheduledClass {
id: number
semesterId: number
date: string
timeSlotId: number
classroomId: number
classroomName: string
courseId: number
courseName: string
classId: number
teacherId: number
teacherName: string
createTime: string
updateTime: string
}
//
interface TimeSlot {
id: number
slotCode: string
dayOfWeek: number
startTime: string
endTime: string
slotName: string
section: number
}
// //
const weekDays: WeekDay[] = [ const weekDays: WeekDay[] = [
{ label: '星期一', value: 1 }, { label: '星期一', value: 1 },
@ -390,17 +418,10 @@ const classSearchLoading = ref(false)
const classSearchTimer = ref<number | null>(null) const classSearchTimer = ref<number | null>(null)
const activeTab = ref('course') const activeTab = ref('course')
// //
const getTimeData = ref<{ const allTimeSlots = ref<TimeSlot[]>([])
lists: Array<{ //
timeSlotId: number const scheduledClasses = ref<ScheduledClass[]>([])
classId: number
classroomId: number
courseId: number
semesterId: number
teacherId: number
}>
}>({ lists: [] })
// - // -
const getCourseLists = ref<{ lists: CourseItem[] }>({ lists: [] }) const getCourseLists = ref<{ lists: CourseItem[] }>({ lists: [] })
@ -412,7 +433,7 @@ const selectedCourseId = ref<number | null>(null)
// //
const selectedCourse = computed(() => { const selectedCourse = computed(() => {
if (!selectedCourseId.value) return null if (!selectedCourseId.value) return null
return getCourseLists.value.lists.find(item => item.id === selectedCourseId.value) || null return getCourseLists.value.lists.find(item => item.courseId === selectedCourseId.value) || null
}) })
// //
@ -468,9 +489,7 @@ const currentWeekDateInfo = computed(() => {
if (isNaN(startDate.getTime())) return '日期格式错误' if (isNaN(startDate.getTime())) return '日期格式错误'
// //
// startDate
const weekStartDate = new Date(startDate) const weekStartDate = new Date(startDate)
//
const dayOfWeek = weekStartDate.getDay() // 0 = , 1 = , ..., 6 = const dayOfWeek = weekStartDate.getDay() // 0 = , 1 = , ..., 6 =
const daysToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek // const daysToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek //
@ -527,8 +546,11 @@ const queryParams = reactive({
// //
const handleWeekChange = () => { const handleWeekChange = () => {
//
console.log('周数变化为:', selectedWeekNumber.value) console.log('周数变化为:', selectedWeekNumber.value)
//
if (queryParams.classId && queryParams.semesterId) {
fetchScheduledClasses()
}
} }
// //
@ -542,6 +564,7 @@ const handleClassChange = async () => {
if (queryParams.classId && queryParams.semesterId) { if (queryParams.classId && queryParams.semesterId) {
await fetchCourseLists() await fetchCourseLists()
await fetchRoomLists() await fetchRoomLists()
await fetchScheduledClasses()
} }
} }
@ -555,6 +578,7 @@ const handleSemesterChange = async () => {
if (queryParams.classId && queryParams.semesterId) { if (queryParams.classId && queryParams.semesterId) {
await fetchCourseLists() await fetchCourseLists()
await fetchScheduledClasses()
} }
} }
@ -604,7 +628,7 @@ const fetchConfigLists = async () => {
const fetchSemester = async () => { const fetchSemester = async () => {
try { try {
const res = await timeCurrentSemester() const res = await timeNextSemester()
if (res) { if (res) {
queryParams.semesterId = res.id queryParams.semesterId = res.id
} }
@ -653,7 +677,8 @@ const fetchCourseLists = async () => {
// / // /
const handleCourseClick = (course: CourseItem) => { const handleCourseClick = (course: CourseItem) => {
selectedCourseId.value = selectedCourseId.value === course.id ? null : course.id selectedCourseId.value = selectedCourseId.value === course.courseId ? null : course.courseId
console.log('课程ID', selectedCourseId)
selectedWeekNumber.value = 1 selectedWeekNumber.value = 1
activeTab.value = 'course' activeTab.value = 'course'
} }
@ -696,16 +721,66 @@ const handleClassroomClick = (classroom: ClassroomItem) => {
activeTab.value = 'classroom' activeTab.value = 'classroom'
} }
//
const fetchScheduledClasses = async () => {
if (!queryParams.classId || !queryParams.semesterId) return
try {
loading.value = true
const res = await courseScheduleList({
classId: queryParams.classId,
semesterId: queryParams.semesterId
})
scheduledClasses.value = res
} catch (err) {
console.error('获取排课数据失败', err)
feedback.msgError('获取排课数据失败')
scheduledClasses.value = []
} finally {
loading.value = false
}
}
//
const fetchTimeSlots = async () => {
try {
const res = await courseAvailableSlots()
allTimeSlots.value = res
} catch (err) {
console.error('获取时间段数据失败', err)
feedback.msgError('获取时间段数据失败')
allTimeSlots.value = []
}
}
//
const getScheduledClass = (section: number, dayOfWeek: number) => {
// ID
const timeSlot = allTimeSlots.value.find(
slot => slot.section === section && slot.dayOfWeek === dayOfWeek
)
if (!timeSlot) return null
//
return scheduledClasses.value.find(item => item.timeSlotId === timeSlot.id) || null
}
// //
const handleTimeSlotClick = (rowIndex: number, dayOfWeek: number, cellData: any) => { const handleTimeSlotClick = (row: any, dayOfWeek: number) => {
const row = tableData.value[rowIndex] //
const id = cellData?.id || Date.now() const timeSlot = allTimeSlots.value.find(
const startTime = getRowStartTime(row) slot => slot.section === row.section && slot.dayOfWeek === dayOfWeek
const endTime = getRowEndTime(row) )
if (!timeSlot) return
//
const scheduledClass = getScheduledClass(row.section, dayOfWeek)
// //
if (selectedTimeSlot.value && if (selectedTimeSlot.value &&
selectedTimeSlot.value.rowIndex === rowIndex && selectedTimeSlot.value.section === row.section &&
selectedTimeSlot.value.dayOfWeek === dayOfWeek) { selectedTimeSlot.value.dayOfWeek === dayOfWeek) {
selectedTimeSlot.value = null selectedTimeSlot.value = null
return return
@ -713,32 +788,28 @@ const handleTimeSlotClick = (rowIndex: number, dayOfWeek: number, cellData: any)
// //
selectedTimeSlot.value = { selectedTimeSlot.value = {
id, id: timeSlot.id,
rowIndex, section: row.section,
dayOfWeek, dayOfWeek,
startTime, startTime: row.startTime,
endTime, endTime: row.endTime,
hasCourse: !!cellData, hasCourse: !!scheduledClass,
courseData: cellData courseData: scheduledClass
} }
// courseId // courseId
if (cellData && cellData.courseId) { if (scheduledClass && scheduledClass.courseId) {
const targetCourse = getCourseLists.value.lists.find(item => item.courseId === cellData.courseId) const targetCourse = getCourseLists.value.lists.find(item => item.courseId === scheduledClass.courseId)
if (targetCourse) { if (targetCourse) {
selectedCourseId.value = targetCourse.id selectedCourseId.value = targetCourse.id
// 1
if (targetCourse.totalWeeks > 0) {
selectedWeekNumber.value = 1
}
} }
} }
} }
// //
const isTimeSlotSelected = (rowIndex: number, dayOfWeek: number, cellData: any) => { const isTimeSlotSelected = (section: number, dayOfWeek: number) => {
return selectedTimeSlot.value && return selectedTimeSlot.value &&
selectedTimeSlot.value.rowIndex === rowIndex && selectedTimeSlot.value.section === section &&
selectedTimeSlot.value.dayOfWeek === dayOfWeek selectedTimeSlot.value.dayOfWeek === dayOfWeek
} }
@ -752,7 +823,7 @@ const getSelectedTimeSlotText = () => {
const slot = selectedTimeSlot.value const slot = selectedTimeSlot.value
if (!slot) return '' if (!slot) return ''
const dayText = weekDays.find(day => day.value === slot.dayOfWeek)?.label || '' const dayText = weekDays.find(day => day.value === slot.dayOfWeek)?.label || ''
return `${slot.rowIndex + 1}${dayText} ${slot.startTime}-${slot.endTime}` return `${slot.section}${dayText} ${slot.startTime}-${slot.endTime}`
} }
// //
@ -776,8 +847,8 @@ const handleScheduleCourse = async () => {
}) })
feedback.msgSuccess('排课成功') feedback.msgSuccess('排课成功')
// //
await getTimeLists() await fetchScheduledClasses()
// //
clearSelectedTimeSlot() clearSelectedTimeSlot()
selectedCourseId.value = null selectedCourseId.value = null
@ -797,12 +868,15 @@ const handleRemoveCourse = async () => {
} }
try { try {
// TODO: API // APIAPIID
// await removeCourse({ timeSlotId: selectedTimeSlot.value.id }) if (selectedTimeSlot.value.courseData && selectedTimeSlot.value.courseData.id) {
// API
feedback.msgSuccess('课程移除成功') // await removeCourse({ id: selectedTimeSlot.value.courseData.id })
await getTimeLists()
clearSelectedTimeSlot() feedback.msgSuccess('课程移除成功')
await fetchScheduledClasses()
clearSelectedTimeSlot()
}
} catch (error) { } catch (error) {
console.error('移除课程失败', error) console.error('移除课程失败', error)
feedback.msgError('移除课程失败,请稍后重试') feedback.msgError('移除课程失败,请稍后重试')
@ -823,64 +897,40 @@ const { pager, getLists } = usePaging({
size: 7 size: 7
}) })
// //
const getTimeLists = async () => { const tableData = computed(() => {
loading.value = true //
try { const sectionMap = new Map<number, { section: number; startTime: string; endTime: string }>()
const res = await courseAvailableSlots()
allLists.value = res //
return Promise.resolve(res) allTimeSlots.value.forEach(slot => {
} catch (err) { if (!sectionMap.has(slot.section)) {
console.error('获取时间段列表失败', err) sectionMap.set(slot.section, {
return Promise.reject(err) section: slot.section,
} finally { startTime: slot.startTime.slice(0, -3), //
loading.value = false endTime: slot.endTime.slice(0, -3) //
})
}
})
//
return Array.from(sectionMap.values()).sort((a, b) => a.section - b.section)
})
//
const initData = async () => {
await fetchTimeSlots()
await fetchClassLists('')
await fetchConfigLists()
//
if (queryParams.classId && queryParams.semesterId) {
await fetchScheduledClasses()
} }
} }
// //
const tableData = computed(() => { initData()
const timeSlotMap = new Map<string, Record<number, any>>()
allLists.value.forEach((item) => {
const key = `${item.startTime}-${item.endTime}`
if (!timeSlotMap.has(key)) {
timeSlotMap.set(key, {})
}
const slotGroup = timeSlotMap.get(key)!
slotGroup[item.dayOfWeek] = item
})
return Array.from(timeSlotMap.values()).sort((a, b) => {
const aTime = Object.values(a)[0]?.startTime || ''
const bTime = Object.values(b)[0]?.startTime || ''
return aTime.localeCompare(bTime)
})
})
const getRowBaseInfo = (row: Record<number, any>) => {
return Object.values(row).find((item) => item) || {}
}
const getRowStartTime = (row: Record<number, any>) => {
const baseInfo = getRowBaseInfo(row)
const start = baseInfo?.startTime
if (start == null) return ''
const s = String(start)
return s.length > 3 ? s.slice(0, -3) : ''
}
const getRowEndTime = (row: Record<number, any>) => {
const baseInfo = getRowBaseInfo(row)
const end = baseInfo?.endTime
if (end == null) return ''
const s = String(end)
return s.length > 3 ? s.slice(0, -3) : ''
}
//
getTimeLists()
fetchClassLists('')
fetchConfigLists()
</script> </script>
<style scoped> <style scoped>
:deep(.first-column) { :deep(.first-column) {

View File

@ -75,8 +75,7 @@ const formData = reactive({
sort: 0, sort: 0,
status: 1, status: 1,
remark: '', remark: '',
typeId: 0, typeId: 0
type_id: 0
}) })
const rules = { const rules = {
@ -98,6 +97,7 @@ const rules = {
const handleSubmit = async () => { const handleSubmit = async () => {
await formRef.value?.validate() await formRef.value?.validate()
console.log(formData)
mode.value == 'edit' ? await dictDataEdit(formData) : await dictDataAdd(formData) mode.value == 'edit' ? await dictDataEdit(formData) : await dictDataAdd(formData)
popupRef.value?.close() popupRef.value?.close()
emit('success') emit('success')
@ -117,7 +117,8 @@ const setFormData = (data: Record<any, any>) => {
if (data[key] != null && data[key] != undefined) { if (data[key] != null && data[key] != undefined) {
//@ts-ignore //@ts-ignore
formData[key] = data[key] formData[key] = data[key]
formData.typeId = data.type_id console.log('data:', data)
console.log('formData:', formData)
} }
} }
} }

View File

@ -149,7 +149,7 @@ import { configDetail, configLists } from '@/api/config'
import { courseDetail } from '@/api/course' import { courseDetail } from '@/api/course'
import { taskAdd, taskCourseList, taskDetail, taskEdit, taskTeacherList } from '@/api/task' import { taskAdd, taskCourseList, taskDetail, taskEdit, taskTeacherList } from '@/api/task'
import { teacherDetail } from '@/api/teacher' import { teacherDetail } from '@/api/teacher'
import { timeCurrentSemester } from '@/api/time' import { timeNextSemester } from '@/api/time'
import Popup from '@/components/popup/index.vue' import Popup from '@/components/popup/index.vue'
import feedback from '@/utils/feedback' import feedback from '@/utils/feedback'
defineProps({ defineProps({
@ -285,7 +285,7 @@ const fetchConfigLists = async () => {
} }
const fetchSemester = async () => { const fetchSemester = async () => {
try { try {
const res = await timeCurrentSemester() const res = await timeNextSemester()
if (res) { if (res) {
formData.semesterId = res.id formData.semesterId = res.id
} }

View File

@ -144,7 +144,7 @@ import { configLists } from '@/api/config'
import { courseLists } from '@/api/course' import { courseLists } from '@/api/course'
import { taskDelete, taskLists, taskSearch } from '@/api/task' import { taskDelete, taskLists, taskSearch } from '@/api/task'
import { teacherLists } from '@/api/teacher' import { teacherLists } from '@/api/teacher'
import { timeCurrentSemester } from '@/api/time' import { timeNextSemester } from '@/api/time'
import { usePaging } from '@/hooks/usePaging' import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback' import feedback from '@/utils/feedback'
@ -172,7 +172,7 @@ const queryParams = reactive({
semesterId: '', semesterId: '',
courseId: '', courseId: '',
teacherId: '', teacherId: '',
classId: '', classId: Number(route.query.classId) || null,
weeklyHours: '', weeklyHours: '',
totalWeeks: '', totalWeeks: '',
createdBy: '', createdBy: '',
@ -205,9 +205,13 @@ const fetchConfigLists = async () => {
} }
const fetchSemester = async () => { const fetchSemester = async () => {
try { try {
const res = await timeCurrentSemester() const res = await timeNextSemester()
console.log('res',res)
if (res) { if (res) {
queryParams.semesterId = res.id queryParams.semesterId = res.id
console.log('semesterId',queryParams.semesterId)
await getClassDetail()
await getLists()
} }
} catch (error) { } catch (error) {
console.error('获取学期信息失败', error) console.error('获取学期信息失败', error)
@ -266,13 +270,14 @@ onBeforeUnmount(() => {
const getClassDetail = async () => { const getClassDetail = async () => {
try { try {
console.log('route.query.classId', route.query.classId) console.log('route.query.classId', route.query.classId)
const res = await classDetail({ const res = await classDetail({id: Number(route.query.classId)})
id: Number(route.query.classId),
semesterId: queryParams.semesterId
})
classData.classCode = res.classCode classData.classCode = res.classCode
classData.className = res.className classData.className = res.className
const progressRes = await taskSearch({ classId: Number(route.query.classId) }) console.log('queryParams.semesterId',queryParams.semesterId)
const progressRes = await taskSearch({
classId: Number(route.query.classId),
semesterId: queryParams.semesterId
})
console.log('progressRes', progressRes) console.log('progressRes', progressRes)
classData.classProgress = progressRes.arrangementProgress || '暂无进度信息' classData.classProgress = progressRes.arrangementProgress || '暂无进度信息'
} catch (err) { } catch (err) {
@ -314,8 +319,6 @@ const handleDelete = async (id: number) => {
} }
fetchConfigLists() fetchConfigLists()
getClassDetail()
fetchCourseLists('') fetchCourseLists('')
fetchTeacherLists('') fetchTeacherLists('')
getLists()
</script> </script>

View File

@ -98,7 +98,9 @@ import type { PropType } from 'vue'
import { courseAdd, courseDetail, courseEdit, courseUploadFile } from '@/api/course' import { courseAdd, courseDetail, courseEdit, courseUploadFile } from '@/api/course'
import { typeLists } from '@/api/courseType' import { typeLists } from '@/api/courseType'
import { requirementLists } from '@/api/requirement' import { requirementLists } from '@/api/requirement'
import Popup from '@/components/popup/index.vue' import Popup from '@/components/popup/index.vue'
import { useDictOptions } from '@/hooks/useDictOptions' import { useDictOptions } from '@/hooks/useDictOptions'
import feedback from '@/utils/feedback' import feedback from '@/utils/feedback'