Browse Source

Merge branch 'master' of http://gitlab.frkj.cc/php/zhjwxt

# Conflicts:
#	niucloud/app/adminapi/controller/course/Course.php
#	niucloud/app/adminapi/route/course.php
#	niucloud/app/service/admin/course/CourseService.php
master
于宏哲PHP 10 months ago
parent
commit
6ba87727cd
  1. 8
      admin/src/app/api/classroom.ts
  2. 3
      admin/src/app/api/course.ts
  3. 2
      admin/src/app/api/course_schedule.ts
  4. 4
      admin/src/app/api/customer_resources.ts
  5. 11
      admin/src/app/api/lesson_course_teaching.ts
  6. 6
      admin/src/app/lang/zh-cn/children_like.children_like.json
  7. 10
      admin/src/app/lang/zh-cn/course.course.json
  8. 42
      admin/src/app/lang/zh-cn/course_schedule.course_schedule.json
  9. 6
      admin/src/app/lang/zh-cn/future_ontent.future_ontent.json
  10. 6
      admin/src/app/lang/zh-cn/instructional_material.instructional_material.json
  11. 6
      admin/src/app/lang/zh-cn/lesson_course_teaching.lesson_course_teaching.json
  12. 6
      admin/src/app/lang/zh-cn/physical_testing.physical_testing.json
  13. 6
      admin/src/app/lang/zh-cn/professional_skills.professional_skills.json
  14. 26
      admin/src/app/views/binding_personnel/binding_personnel.vue
  15. 333
      admin/src/app/views/binding_test_paper/binding_test_paper.vue
  16. 33
      admin/src/app/views/children_like/children_like.vue
  17. 273
      admin/src/app/views/children_like/components/basketball-course-teaching-edit.vue
  18. 271
      admin/src/app/views/children_like/components/ninja-teaching-edit.vue
  19. 271
      admin/src/app/views/children_like/components/physical-teaching-edit.vue
  20. 273
      admin/src/app/views/children_like/components/security-teaching-edit.vue
  21. 273
      admin/src/app/views/children_like/components/strengthen-course-teaching-edit.vue
  22. 1032
      admin/src/app/views/children_like/physical_testing.vue
  23. 390
      admin/src/app/views/course_schedule/components/course-schedule-edit.vue
  24. 184
      admin/src/app/views/course_schedule/course_schedule.vue
  25. 271
      admin/src/app/views/future_ontent/components/basketball-course-teaching-edit.vue
  26. 267
      admin/src/app/views/future_ontent/components/ninja-teaching-edit.vue
  27. 271
      admin/src/app/views/future_ontent/components/physical-teaching-edit.vue
  28. 271
      admin/src/app/views/future_ontent/components/security-teaching-edit.vue
  29. 269
      admin/src/app/views/future_ontent/components/strengthen-course-teaching-edit.vue
  30. 32
      admin/src/app/views/future_ontent/future_ontent.vue
  31. 1952
      admin/src/app/views/future_ontent/lesson_course_teaching.vue
  32. 267
      admin/src/app/views/instructional_material/components/ninja-teaching-edit.vue
  33. 271
      admin/src/app/views/instructional_material/components/physical-teaching-edit.vue
  34. 271
      admin/src/app/views/instructional_material/components/security-teaching-edit.vue
  35. 269
      admin/src/app/views/instructional_material/components/strengthen-course-teaching-edit.vue
  36. 39
      admin/src/app/views/instructional_material/instructional_material.vue
  37. 1952
      admin/src/app/views/instructional_material/lesson_course_teaching.vue
  38. 73
      admin/src/app/views/lesson_course_teaching/lesson_course_teaching.vue
  39. 32
      admin/src/app/views/order_table/order_table.vue
  40. 271
      admin/src/app/views/physical_testing/components/ninja-teaching-edit.vue
  41. 271
      admin/src/app/views/physical_testing/components/physical-teaching-edit.vue
  42. 273
      admin/src/app/views/physical_testing/components/security-teaching-edit.vue
  43. 273
      admin/src/app/views/physical_testing/components/strengthen-course-teaching-edit.vue
  44. 40
      admin/src/app/views/physical_testing/physical_testing.vue
  45. 1736
      admin/src/app/views/physical_testing/professional_skills.vue
  46. 271
      admin/src/app/views/professional_skills/components/physical-teaching-edit.vue
  47. 789
      admin/src/app/views/professional_skills/future_ontent.vue
  48. 62
      admin/src/app/views/professional_skills/professional_skills.vue
  49. 415
      admin/src/app/views/timetables/components/schedule-add.vue
  50. 486
      admin/src/app/views/timetables/components/seat-selector.vue
  51. 23
      admin/src/app/views/timetables/timetables.vue
  52. 113
      admin/src/utils/timeslots.ts
  53. 6642
      admin/yarn.lock
  54. 102
      niucloud/app/adminapi/controller/classroom/Classroom.php
  55. 97
      niucloud/app/adminapi/controller/course/Course.php
  56. 101
      niucloud/app/adminapi/controller/course_schedule/CourseSchedule.php
  57. 195
      niucloud/app/adminapi/controller/customer_resources/CustomerResources.php
  58. 19
      niucloud/app/adminapi/controller/lesson_course_teaching/LessonCourseTeaching.php
  59. 11
      niucloud/app/adminapi/controller/student/Student.php
  60. 2
      niucloud/app/adminapi/controller/venue/Venue.php
  61. 65
      niucloud/app/adminapi/route/class.php
  62. 4
      niucloud/app/adminapi/route/classroom.php
  63. 3
      niucloud/app/adminapi/route/course.php
  64. 2
      niucloud/app/adminapi/route/course_schedule.php
  65. 3
      niucloud/app/adminapi/route/customer_resources.php
  66. 3
      niucloud/app/adminapi/route/lesson_course_teaching.php
  67. 2
      niucloud/app/adminapi/route/student.php
  68. 161
      niucloud/app/api/controller/apiController/Attendance.php
  69. 7
      niucloud/app/api/route/route.php
  70. 11
      niucloud/app/common.php
  71. 39
      niucloud/app/model/attendance/Attendance.php
  72. 91
      niucloud/app/model/chat_friends/ChatFriends.php
  73. 50
      niucloud/app/model/chat_messages/ChatMessages.php
  74. 8
      niucloud/app/model/class_grade/ClassGrade.php
  75. 148
      niucloud/app/model/classroom/Classroom.php
  76. 140
      niucloud/app/model/course_schedule/CourseSchedule.php
  77. 32
      niucloud/app/model/rel/ClassPersonnelRel.php
  78. 6
      niucloud/app/model/student/Student.php
  79. 2
      niucloud/app/model/venue/Venue.php
  80. 48
      niucloud/app/service/admin/classroom/ClassroomService.php
  81. 10
      niucloud/app/service/admin/course/CourseService.php
  82. 172
      niucloud/app/service/admin/course_schedule/CourseScheduleService.php
  83. 139
      niucloud/app/service/admin/customer_resources/CustomerResourcesService.php
  84. 22
      niucloud/app/service/admin/lesson_course_teaching/LessonCourseTeachingService.php
  85. 1
      niucloud/app/service/admin/pay/PayService.php
  86. 45
      niucloud/app/service/admin/student/StudentService.php
  87. 99
      niucloud/app/service/admin/venue/VenueService.php
  88. 156
      niucloud/app/service/api/apiService/AttendanceService.php
  89. 32
      niucloud/app/service/api/apiService/CommonService.php
  90. 4
      niucloud/app/service/api/apiService/TeachingResearchService.php
  91. 6
      niucloud/app/validate/course_schedule/CourseSchedule.php
  92. 6
      niucloud/public/.gitignore
  93. 1
      niucloud/public/addon/README.md
  94. BIN
      niucloud/public/addon/shop/Union.png
  95. BIN
      niucloud/public/addon/shop/VIP.png
  96. BIN
      niucloud/public/addon/shop/apply/tiaojian.png
  97. BIN
      niucloud/public/addon/shop/attachment/active_cube_01.png
  98. BIN
      niucloud/public/addon/shop/attachment/active_cube_02.png
  99. BIN
      niucloud/public/addon/shop/attachment/active_cube_03.png
  100. BIN
      niucloud/public/addon/shop/attachment/active_cube_04.png

8
admin/src/app/api/classroom.ts

@ -66,4 +66,12 @@ export function getWithPersonnelList(params: Record<string, any>) {
export function getAllClassroomList() { export function getAllClassroomList() {
return request.get('classroom/classroom_all') return request.get('classroom/classroom_all')
} }
export function getClassroompeople($class_id: number) {
return request.get(`classroom/getClassroompeople/${$class_id}`)
}
export function getClassroompeopleCount($class_id: number,params: Record<string, any>) {
return request.get(`classroom/getClassroompeopleCount/${$class_id}`,{params})
}
// USER_CODE_END -- class // USER_CODE_END -- class

3
admin/src/app/api/course.ts

@ -62,4 +62,7 @@ export function deleteCourse(id: number) {
}) })
} }
export function getAllCourseList(params: Record<string, any>) {
return request.get(`course/getAllCourseList`, { params })
}
// USER_CODE_END -- course // USER_CODE_END -- course

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

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

4
admin/src/app/api/customer_resources.ts

@ -72,3 +72,7 @@ export function fpEdit(params: Record<string, any>) {
showSuccessMessage: true, showSuccessMessage: true,
}) })
} }
export function getWithCoachList(params: Record<string, any>) {
return request.get('customer_resources/coach_person', { params })
}

11
admin/src/app/api/lesson_course_teaching.ts

@ -328,4 +328,15 @@ export function getWithPersonnelDataList(params: Record<string, any>) {
return request.get('lesson_course_teaching/personnel_data_all', { params }) return request.get('lesson_course_teaching/personnel_data_all', { params })
} }
export function getWithTestPaperList(params: Record<string, any>) {
return request.get('lesson_course_teaching/test_paper', { params })
}
export function setBindingTestPaperModule(params: Record<string, any>) {
return request.put(
`lesson_course_teaching/binding_test_module/${params.id}`,
params
)
}
// USER_CODE_END -- lesson_course_teaching // USER_CODE_END -- lesson_course_teaching

6
admin/src/app/lang/zh-cn/children_like.children_like.json

@ -33,5 +33,9 @@
"bindingModule": "绑定模块", "bindingModule": "绑定模块",
"url1": "图片上传", "url1": "图片上传",
"url2": "文件上传", "url2": "文件上传",
"url3": "视频上传" "url3": "视频上传",
"addBindingTestPaper": "绑定试卷",
"totalScore": "总分",
"selectionMode": "题目选择模式",
"passingScore": "合格分数"
} }

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

@ -23,5 +23,13 @@
"updateCourse": "编辑课程", "updateCourse": "编辑课程",
"courseDeleteTips": "确定要删除该数据吗?", "courseDeleteTips": "确定要删除该数据吗?",
"startDate": "请选择开始时间", "startDate": "请选择开始时间",
"endDate": "请选择结束时间" "endDate": "请选择结束时间",
"pending": "待开始",
"upcoming": "即将开始",
"ongoing": "进行中",
"completed": "已结束",
"autoSchedule": "自动排课",
"autoSchedulePlaceholder": "请选择是否自动排课",
"yes": "是",
"no": "否"
} }

42
admin/src/app/lang/zh-cn/course_schedule.course_schedule.json

@ -1,29 +1,37 @@
{ {
"id": "课程安排编号", "id": "课程安排编号",
"idPlaceholder": "请输入课程安排编号", "idPlaceholder": "请输入课程安排编号",
"campusId": "校区ID", "campusId": "校区",
"campusIdPlaceholder": "请输入校区ID", "campusIdPlaceholder": "请选择校区",
"venueId": "场地ID", "venueId": "场地",
"venueIdPlaceholder": "请输入场地ID", "venueIdPlaceholder": "请选择场地",
"courseDate": "上课日期", "courseDate": "上课日期",
"courseDatePlaceholder": "请输入上课日期", "courseDatePlaceholder": "请选择上课日期",
"timeSlot": "上课时段", "timeSlot": "上课时段",
"timeSlotPlaceholder": "请输入上课时段", "timeSlotPlaceholder": "请选择上课时段",
"courseId": "课程ID", "courseId": "课程",
"courseIdPlaceholder": "请输入课程ID", "courseIdPlaceholder": "请选择课程",
"coachId": "上课教练ID", "coachId": "上课教练",
"coachIdPlaceholder": "请输入上课教练ID", "coachIdPlaceholder": "请选择上课教练",
"participants": "参与人员列表", "participants": "参与人员",
"participantsPlaceholder": "请输入参与人员列表", "participantsPlaceholder": "请选择参与人员",
"studentIds": "上课学生列表", "studentIds": "参与学生",
"studentIdsPlaceholder": "请输入上课学生列表", "studentIdsPlaceholder": "请选择参与学生",
"availableCapacity": "根据场地容量判断的可安排学员位置数量", "availableCapacity": "根据场地容量判断的可安排学员位置数量",
"availableCapacityPlaceholder": "请输入根据场地容量判断的可安排学员位置数量", "availableCapacityPlaceholder": "请输入根据场地容量判断的可安排学员位置数量",
"status": "课程状态:", "status": "课程状态",
"statusPlaceholder": "请输入课程状态:", "statusPlaceholder": "请选择课程状态",
"addCourseSchedule": "添加课程安排", "addCourseSchedule": "添加课程安排",
"updateCourseSchedule": "编辑课程安排", "updateCourseSchedule": "编辑课程安排",
"courseScheduleDeleteTips": "确定要删除该数据吗?", "courseScheduleDeleteTips": "确定要删除该数据吗?",
"startDate": "请选择开始时间", "startDate": "请选择开始时间",
"endDate": "请选择结束时间" "endDate": "请选择结束时间",
"pending": "待开始",
"upcoming": "即将开始",
"ongoing": "进行中",
"completed": "已结束",
"autoSchedule": "自动排课",
"autoSchedulePlaceholder": "请选择是否自动排课",
"yes": "是",
"no": "否"
} }

6
admin/src/app/lang/zh-cn/future_ontent.future_ontent.json

@ -33,5 +33,9 @@
"bindingModule": "绑定模块", "bindingModule": "绑定模块",
"url1": "图片上传", "url1": "图片上传",
"url2": "文件上传", "url2": "文件上传",
"url3": "视频上传" "url3": "视频上传",
"addBindingTestPaper": "绑定试卷",
"totalScore": "总分",
"selectionMode": "题目选择模式",
"passingScore": "合格分数"
} }

6
admin/src/app/lang/zh-cn/instructional_material.instructional_material.json

@ -35,5 +35,9 @@
"bindingModule": "绑定模块", "bindingModule": "绑定模块",
"url1": "图片上传", "url1": "图片上传",
"url2": "文件上传", "url2": "文件上传",
"url3": "视频上传" "url3": "视频上传",
"addBindingTestPaper": "绑定试卷",
"totalScore": "总分",
"selectionMode": "题目选择模式",
"passingScore": "合格分数"
} }

6
admin/src/app/lang/zh-cn/lesson_course_teaching.lesson_course_teaching.json

@ -43,5 +43,9 @@
"bindingModule": "绑定模块", "bindingModule": "绑定模块",
"url1": "图片上传", "url1": "图片上传",
"url2": "文件上传", "url2": "文件上传",
"url3": "视频上传" "url3": "视频上传",
"addBindingTestPaper": "绑定试卷",
"totalScore": "总分",
"selectionMode": "题目选择模式",
"passingScore": "合格分数"
} }

6
admin/src/app/lang/zh-cn/physical_testing.physical_testing.json

@ -35,5 +35,9 @@
"bindingModule": "绑定模块", "bindingModule": "绑定模块",
"url1": "图片上传", "url1": "图片上传",
"url2": "文件上传", "url2": "文件上传",
"url3": "视频上传" "url3": "视频上传",
"addBindingTestPaper": "绑定试卷",
"totalScore": "总分",
"selectionMode": "题目选择模式",
"passingScore": "合格分数"
} }

6
admin/src/app/lang/zh-cn/professional_skills.professional_skills.json

@ -41,5 +41,9 @@
"bindingModule": "绑定模块", "bindingModule": "绑定模块",
"url1": "图片上传", "url1": "图片上传",
"url2": "文件上传", "url2": "文件上传",
"url3": "视频上传" "url3": "视频上传",
"addBindingTestPaper": "绑定试卷",
"totalScore": "总分",
"selectionMode": "题目选择模式",
"passingScore": "合格分数"
} }

26
admin/src/app/views/binding_personnel/binding_personnel.vue

@ -83,16 +83,16 @@
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> />
<el-table-column :label="t('operation')" fixed="right" min-width="120"> <!-- <el-table-column :label="t('operation')" fixed="right" min-width="120">-->
<template #default="{ row }"> <!-- <template #default="{ row }">-->
<el-button type="primary" link @click="editEvent(row)">{{ <!-- <el-button type="primary" link @click="editEvent(row)">{{-->
t('edit') <!-- t('edit')-->
}}</el-button> <!-- }}</el-button>-->
<el-button type="primary" link @click="deleteEvent(row.id, 1)">{{ <!-- <el-button type="primary" link @click="deleteEvent(row.id, 1)">{{-->
t('delete') <!-- t('delete')-->
}}</el-button> <!-- }}</el-button>-->
</template> <!-- </template>-->
</el-table-column> <!-- </el-table-column>-->
</el-table> </el-table>
<div class="mt-[16px] flex justify-end"> <div class="mt-[16px] flex justify-end">
<el-pagination <el-pagination
@ -167,7 +167,7 @@ import {
getWithPersonnelDataList, getWithPersonnelDataList,
setBindingModule, setBindingModule,
} from '@/app/api/lesson_course_teaching' } from '@/app/api/lesson_course_teaching'
import { ElMessage, ElMessageBox } from 'element-plus';
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
@ -257,6 +257,10 @@ const emit = defineEmits(['complete'])
* @param formEl * @param formEl
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if(multipleSelection.value.length == 0) {
ElMessage.error('请选择数据');
return;
}
let data = { let data = {
id: BindingId.value, id: BindingId.value,
user_permission: multipleSelection.value user_permission: multipleSelection.value

333
admin/src/app/views/binding_test_paper/binding_test_paper.vue

@ -0,0 +1,333 @@
<template>
<el-dialog
v-model="showDialog"
:title="t('addBindingTestPaper')"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:inline="true"
:model="lessonCourseTeachingTable.searchParam"
ref="searchFormRef"
>
<el-form-item :label="t('totalScore')" prop="title">
<el-input
v-model="lessonCourseTeachingTable.searchParam.total_score"
:placeholder="t('totalScore')"
/>
</el-form-item>
<el-form-item :label="t('selectionMode')" prop="status">
<el-select class="input-width" v-model="lessonCourseTeachingTable.searchParam.selection_mode" clearable :placeholder="t('selectionMode')">
<el-option label="请选择" value=""></el-option>
<el-option label="随机组题" value="random" />
<el-option label="自选题目" value="manual" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadLessonCourseTeachingList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
</el-form-item>
</el-form>
<div class="mt-[10px]">
<el-table
:data="lessonCourseTeachingTable.data"
size="large"
v-loading="lessonCourseTeachingTable.loading"
@selection-change="handleSelectionChange"
>
<template #empty>
<span>{{
!lessonCourseTeachingTable.loading ? t('emptyData') : ''
}}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column
prop="total_score"
min-width="80"
:label="t('totalScore')"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="passing_score"
min-width="80"
:label="t('passingScore')"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('selectionMode')"
min-width="120"
align="center"
:show-overflow-tooltip="true"
>
<template #default="{ row }">
<div v-if="row.selection_mode == 'random'">随机组题</div>
<div v-if="row.selection_mode == 'manual'">自选题目</div>
</template>
</el-table-column>
<el-table-column
prop="created_at"
:label="t('userCreateTime')"
min-width="150"
:show-overflow-tooltip="true"
/>
<!-- <el-table-column :label="t('operation')" fixed="right" 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, 1)">{{-->
<!-- t('delete')-->
<!-- }}</el-button>-->
<!-- </template>-->
<!-- </el-table-column>-->
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="lessonCourseTeachingTable.page"
v-model:page-size="lessonCourseTeachingTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="lessonCourseTeachingTable.total"
@size-change="loadLessonCourseTeachingList()"
@current-change="loadLessonCourseTeachingList"
/>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addLessonCourseTeaching,
editLessonCourseTeaching,
getLessonCourseTeachingInfo,
getLessonCourseTeachingList,
getWithPersonnelDataList, getWithTestPaperList,
setBindingTestPaperModule,
} from '@/app/api/lesson_course_teaching'
import { ElMessage } from 'element-plus'
let showDialog = ref(false)
const loading = ref(false)
let lessonCourseTeachingTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
total_score: '',
selection_mode: ''
},
})
const multipleSelection = ref<[]>([])
const binding_module = ref('')
const handleSelectionChange = (val: []) => {
multipleSelection.value = val
}
const loadLessonCourseTeachingList = (page: number = 1) => {
lessonCourseTeachingTable.loading = true
lessonCourseTeachingTable.page = page
getWithTestPaperList({
page: lessonCourseTeachingTable.page,
limit: lessonCourseTeachingTable.limit,
...lessonCourseTeachingTable.searchParam,
})
.then((res) => {
lessonCourseTeachingTable.loading = false
lessonCourseTeachingTable.data = res.data.data
lessonCourseTeachingTable.total = res.data.total
})
.catch(() => {
lessonCourseTeachingTable.loading = false
})
}
loadLessonCourseTeachingList()
const resetForm = (page: number = 1) => {
lessonCourseTeachingTable.searchParam.total_score = ''
lessonCourseTeachingTable.searchParam.selection_mode = ''
loadLessonCourseTeachingList()
}
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
// user_permission: [],
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if(multipleSelection.value.length == 0) {
ElMessage.error('请选择数据');
return;
} else if (multipleSelection.value.length > 1) {
ElMessage.error('只能选择一条数据');
return;
}
let data = {
id: BindingId.value,
exam_papers_id: multipleSelection.value
.map((item) => item.id)
.join(',')
}
setBindingTestPaperModule(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const BindingId = ref(undefined)
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
BindingId.value = row.id
binding_module.value = row.table_type
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

33
admin/src/app/views/children_like/children_like.vue

@ -127,7 +127,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ <el-button type="primary" link @click="editEvent(row)">{{
@ -145,6 +145,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -290,7 +295,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -311,6 +316,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -456,7 +466,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -477,6 +487,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -503,6 +518,10 @@
ref="BindingPersonnelDialog" ref="BindingPersonnelDialog"
@complete="loadLessonCourseTeachingList" @complete="loadLessonCourseTeachingList"
/> />
<binding-test-paper
ref="BindingTestPaperDialog"
@complete="loadLessonCourseTeachingList"
/>
</div> </div>
</template> </template>
@ -529,6 +548,7 @@ import EditJump from '@/app/views/children_like/components/Jump-lesson-library-e
import EditEn from '@/app/views/children_like/components/en-course-teaching-edit.vue' import EditEn from '@/app/views/children_like/components/en-course-teaching-edit.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue' import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue'
import bindingTestPaper from '@/app/views/binding_test_paper/binding_test_paper.vue'
const route = useRoute() const route = useRoute()
const pageName = route.meta.title const pageName = route.meta.title
const active = 'CourseSyllabus' const active = 'CourseSyllabus'
@ -601,11 +621,16 @@ const editEnTeachingLibraryDialog: Record<string, any> | null = ref(null)
const editBasketballTeachingLibraryDialog: Record<string, any> | null = const editBasketballTeachingLibraryDialog: Record<string, any> | null =
ref(null) ref(null)
const BindingPersonnelDialog: Record<string, any> | null = ref(null) const BindingPersonnelDialog: Record<string, any> | null = ref(null)
const BindingTestPaperDialog: Record<string, any> | null = ref(null)
const addBindingPersonnel = (row) => { const addBindingPersonnel = (row) => {
BindingPersonnelDialog.value.setFormData(row) BindingPersonnelDialog.value.setFormData(row)
BindingPersonnelDialog.value.showDialog = true BindingPersonnelDialog.value.showDialog = true
} }
const addBindingTestPaper = (row) => {
BindingTestPaperDialog.value.setFormData(row)
BindingTestPaperDialog.value.showDialog = true
}
/** /**
* 获取课程教学大纲列表 * 获取课程教学大纲列表
*/ */

273
admin/src/app/views/children_like/components/basketball-course-teaching-edit.vue

@ -1,273 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editBasketballTeachingLibrary')
: t('addBasketballTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addBasketballTeachingLibrary,
editBasketballTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
import {
addPublicLibraryList,
editPublicLibraryList,
} from '@/app/api/future_ontent'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
table_type: 26,
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editPublicLibraryList : addPublicLibraryList
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
data.table_type = 26
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

271
admin/src/app/views/children_like/components/ninja-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id ? t('editNinjaTeachingLibrary') : t('addNinjaTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addNinjaTeachingLibrary,
editNinjaTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
import {
addPublicLibraryList,
editPublicLibraryList,
} from '@/app/api/future_ontent'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
table_type: 21,
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editPublicLibraryList : addPublicLibraryList
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
data.table_type = 21
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

271
admin/src/app/views/children_like/components/physical-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editPhysicalTeachingLibrary')
: t('addPhysicalTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addPhysicalTeachingLibrary,
editPhysicalTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id
? editPhysicalTeachingLibrary
: addPhysicalTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

273
admin/src/app/views/children_like/components/security-teaching-edit.vue

@ -1,273 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editSecurityTeachingLibrary')
: t('addSecurityTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addSecurityTeachingLibrary,
editSecurityTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
import {
addPublicLibraryList,
editPublicLibraryList,
} from '@/app/api/future_ontent'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
table_type: 22,
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editPublicLibraryList : addPublicLibraryList
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
data.table_type = 22
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

273
admin/src/app/views/children_like/components/strengthen-course-teaching-edit.vue

@ -1,273 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editStrengthenTeachingLibrary')
: t('addStrengthenTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addStrengTeachingLibrary,
editStrengTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
import {
addPublicLibraryList,
editPublicLibraryList,
} from '@/app/api/future_ontent'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
table_type: 20,
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editPublicLibraryList : addPublicLibraryList
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
data.table_type = 20
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

1032
admin/src/app/views/children_like/physical_testing.vue

File diff suppressed because it is too large

390
admin/src/app/views/course_schedule/components/course-schedule-edit.vue

@ -15,93 +15,107 @@
v-loading="loading" v-loading="loading"
> >
<el-form-item :label="t('campusId')" prop="campus_id"> <el-form-item :label="t('campusId')" prop="campus_id">
<el-input <el-select
v-model="formData.campus_id" v-model="formData.campus_id"
clearable clearable
:placeholder="t('campusIdPlaceholder')" :placeholder="t('campusIdPlaceholder')"
class="input-width" class="input-width"
/> >
<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>
<el-form-item :label="t('venueId')" prop="venue_id"> <el-form-item :label="t('venueId')" prop="venue_id">
<el-input <el-select
v-model="formData.venue_id" v-model="formData.venue_id"
clearable clearable
:placeholder="t('venueIdPlaceholder')" :placeholder="t('venueIdPlaceholder')"
class="input-width" class="input-width"
/> >
<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>
<el-form-item :label="t('courseDate')" prop="course_date"> <el-form-item :label="t('courseDate')" prop="course_date">
<el-input <el-date-picker
v-model="formData.course_date" v-model="formData.course_date"
type="date"
clearable clearable
:placeholder="t('courseDatePlaceholder')" :placeholder="t('courseDatePlaceholder')"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
class="input-width" class="input-width"
style="width: 100%"
:disabled-date="disabledDate"
/> />
</el-form-item> </el-form-item>
<el-form-item :label="t('timeSlot')" prop="time_slot"> <el-form-item :label="t('timeSlot')" prop="time_slot">
<el-input <el-select
v-model="formData.time_slot" v-model="formData.time_slot"
clearable clearable
:placeholder="t('timeSlotPlaceholder')" :placeholder="t('timeSlotPlaceholder')"
class="input-width" class="input-width"
/> style="width: 100%"
allow-create
filterable
>
<el-option
v-for="(item, index) in timeSlotOptions"
:key="index"
:label="item"
:value="item"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('courseId')" prop="course_id"> <el-form-item :label="t('courseId')" prop="course_id">
<el-input <el-select
v-model="formData.course_id" v-model="formData.course_id"
clearable clearable
:placeholder="t('courseIdPlaceholder')" :placeholder="t('courseIdPlaceholder')"
class="input-width" class="input-width"
/> >
<el-option
v-for="item in courseList"
:key="item.id"
:label="item.course_name"
:value="item.id"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('coachId')" prop="coach_id"> <el-form-item :label="t('coachId')" prop="coach_id">
<el-input <el-select
v-model="formData.coach_id" v-model="formData.coach_id"
clearable clearable
:placeholder="t('coachIdPlaceholder')" :placeholder="t('coachIdPlaceholder')"
class="input-width" class="input-width"
/> >
</el-form-item> <el-option
v-for="item in coachList"
<el-form-item :label="t('participants')" prop="participants"> :key="item.id"
<el-input :label="item.name"
v-model="formData.participants" :value="item.id"
clearable />
:placeholder="t('participantsPlaceholder')" </el-select>
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('studentIds')" prop="student_ids">
<el-input
v-model="formData.student_ids"
clearable
:placeholder="t('studentIdsPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('availableCapacity')" prop="available_capacity">
<el-input
v-model="formData.available_capacity"
clearable
:placeholder="t('availableCapacityPlaceholder')"
class="input-width"
/>
</el-form-item> </el-form-item>
<el-form-item :label="t('status')" prop="status"> <el-form-item :label="t('autoSchedule')" prop="auto_schedule">
<el-input <el-radio-group v-model="formData.auto_schedule">
v-model="formData.status" <el-radio :label="1">{{ t('yes') }}</el-radio>
clearable <el-radio :label="0">{{ t('no') }}</el-radio>
:placeholder="t('statusPlaceholder')" </el-radio-group>
class="input-width"
/>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -120,7 +134,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue' import { ref, reactive, computed, watch, onMounted } from 'vue'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
@ -129,6 +143,11 @@ import {
editCourseSchedule, editCourseSchedule,
getCourseScheduleInfo, getCourseScheduleInfo,
} from '@/app/api/course_schedule' } from '@/app/api/course_schedule'
import { getWithCampusList, getAllVenueList } from '@/app/api/venue'
import { getVenueInfo } from '@/app/api/venue'
import { generateTimeSlots } from '@/utils/timeslots'
import { getAllCourseList } from '@/app/api/course'
import { getWithCoachList } from '@/app/api/customer_resources'
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
@ -148,6 +167,7 @@ const initialFormData = {
student_ids: '', student_ids: '',
available_capacity: '', available_capacity: '',
status: '', status: '',
auto_schedule: 1, //
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -174,26 +194,9 @@ const formRules = computed(() => {
coach_id: [ coach_id: [
{ required: true, message: t('coachIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('coachIdPlaceholder'), trigger: 'blur' },
], ],
participants: [ auto_schedule: [
{ { required: true, message: t('autoSchedulePlaceholder'), trigger: 'change' },
required: true, ]
message: t('participantsPlaceholder'),
trigger: 'blur',
},
],
student_ids: [
{ required: true, message: t('studentIdsPlaceholder'), trigger: 'blur' },
],
available_capacity: [
{
required: true,
message: t('availableCapacityPlaceholder'),
trigger: 'blur',
},
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
} }
}) })
@ -228,17 +231,234 @@ const confirm = async (formEl: FormInstance | undefined) => {
// //
//
const campusList = ref<any[]>([])
//
const venueList = ref<any[]>([])
//
const timeSlotOptions = ref<string[]>([])
//
const courseList = ref<any[]>([])
//
const coachList = ref<any[]>([])
//
const isInitializing = ref(false);
//
const loadCampusList = () => {
getWithCampusList({})
.then((res) => {
campusList.value = res.data || []
})
.catch(() => {})
}
//
const loadVenueList = (campus_id?: string | number) => {
getAllVenueList({ campus_id })
.then((res) => {
venueList.value = res.data || []
})
.catch(() => {})
}
//
const loadCourseList = () => {
getAllCourseList({})
.then((res) => {
courseList.value = res.data || []
})
.catch(() => {})
}
//
const loadCoachList = (campus_id?: string | number) => {
getWithCoachList({ campus_id })
.then((res) => {
coachList.value = res.data || []
})
.catch(() => {})
}
//
watch(
() => formData.campus_id,
(newValue, oldValue) => {
//
if (isInitializing.value) return;
//
if (newValue !== oldValue) {
formData.venue_id = ''
formData.coach_id = ''
timeSlotOptions.value = []
if (newValue) {
getAllVenueList({ campus_id: newValue })
.then((res) => {
venueList.value = res.data || []
})
.catch(() => {})
getWithCoachList({ campus_id: newValue })
.then((res) => {
coachList.value = res.data || []
})
.catch(() => {})
} else {
venueList.value = []
coachList.value = []
}
}
}
)
//
watch(
() => formData.venue_id,
async (newValue, oldValue) => {
//
if (isInitializing.value) return;
//
if (newValue !== oldValue) {
formData.time_slot = ''
timeSlotOptions.value = []
if (newValue) {
try {
const res = await getVenueInfo(newValue)
if (res.data) {
timeSlotOptions.value = generateTimeSlots(res.data)
}
} catch (error) {
console.error('获取场地详情失败:', error)
}
}
}
}
)
const setFormData = async (row: any = null) => { const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if (row) { isInitializing.value = true
const data = await (await getCourseScheduleInfo(row.id)).data
if (data) try {
Object.keys(formData).forEach((key: string) => { // 1. ()
if (data[key] != undefined) formData[key] = data[key] let detailData = null;
}) if (row) {
const response = await getCourseScheduleInfo(row.id);
detailData = response.data;
}
// 2.
await new Promise<void>((resolve) => {
getWithCampusList({})
.then((res) => {
campusList.value = res.data || [];
resolve();
})
.catch(() => {
resolve();
});
});
// 3.
await new Promise<void>((resolve) => {
getAllCourseList({})
.then((res) => {
courseList.value = res.data || [];
resolve();
})
.catch(() => {
resolve();
});
});
// 4.
if (detailData) {
// 便
const timeSlotValue = detailData.time_slot;
// 4.1
if (detailData.campus_id !== undefined) {
formData.campus_id = detailData.campus_id;
// 4.2
await Promise.all([
new Promise<void>((resolve) => {
getAllVenueList({ campus_id: detailData.campus_id })
.then((res) => {
venueList.value = res.data || [];
resolve();
})
.catch(() => {
resolve();
});
}),
new Promise<void>((resolve) => {
getWithCoachList({ campus_id: detailData.campus_id })
.then((res) => {
coachList.value = res.data || [];
resolve();
})
.catch(() => {
resolve();
});
})
]);
}
// 4.3 ID
if (detailData.venue_id) {
// ID
formData.venue_id = detailData.venue_id;
try {
const res = await getVenueInfo(detailData.venue_id);
if (res.data) {
//
timeSlotOptions.value = generateTimeSlots(res.data);
//
const isValidTimeSlot = timeSlotOptions.value.includes(timeSlotValue);
//
if (isValidTimeSlot) {
formData.time_slot = timeSlotValue;
} else if (timeSlotOptions.value.length > 0) {
formData.time_slot = timeSlotOptions.value[0];
console.warn('原时间段不可用,已选择第一个可用时间段');
} else {
formData.time_slot = '';
console.warn('没有可用的时间段选项');
}
}
} catch (error) {
console.error('获取场地详情失败:', error);
}
}
// 4.4
Object.keys(formData).forEach((key) => {
//
if (key === 'campus_id' || key === 'venue_id' || key === 'time_slot') {
return;
}
if (detailData[key] !== undefined) {
formData[key] = detailData[key];
} else if (key === 'auto_schedule' && detailData[key] === undefined) {
// 使1
formData.auto_schedule = 1;
}
});
}
} catch (error) {
console.error('加载数据失败:', error);
} finally {
loading.value = false;
isInitializing.value = false;
} }
loading.value = false
} }
// //
@ -282,13 +502,35 @@ const numberVerify = (rule: any, value: any, callback: any) => {
} }
} }
//
const disabledDate = (time: Date) => {
const today = new Date()
today.setHours(0, 0, 0, 0)
const currentDate = new Date(time)
currentDate.setHours(0, 0, 0, 0)
//
return today.getTime() > currentDate.getTime()
}
defineExpose({ defineExpose({
showDialog, showDialog,
setFormData, setFormData,
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped>
.input-width {
width: 100%;
}
:deep(.el-date-editor.el-input),
:deep(.el-date-editor.el-input__wrapper),
:deep(.el-select),
:deep(.el-select__wrapper) {
width: 100%;
}
</style>
<style lang="scss"> <style lang="scss">
.diy-dialog-wrap .el-form-item__label { .diy-dialog-wrap .el-form-item__label {
height: auto !important; height: auto !important;

184
admin/src/app/views/course_schedule/course_schedule.vue

@ -18,68 +18,49 @@
ref="searchFormRef" ref="searchFormRef"
> >
<el-form-item :label="t('campusId')" prop="campus_id"> <el-form-item :label="t('campusId')" prop="campus_id">
<el-input <el-select
v-model="courseScheduleTable.searchParam.campus_id" v-model="courseScheduleTable.searchParam.campus_id"
:placeholder="t('campusIdPlaceholder')" :placeholder="t('campusIdPlaceholder')"
/> clearable
>
<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>
<el-form-item :label="t('venueId')" prop="venue_id"> <el-form-item :label="t('venueId')" prop="venue_id">
<el-input <el-select
v-model="courseScheduleTable.searchParam.venue_id" v-model="courseScheduleTable.searchParam.venue_id"
:placeholder="t('venueIdPlaceholder')" :placeholder="t('venueIdPlaceholder')"
/> clearable
>
<el-option
v-for="item in venueFilterList"
:key="item.id"
:label="item.venue_name"
:value="item.id"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('courseDate')" prop="course_date"> <el-form-item :label="t('courseDate')" prop="course_date">
<el-input <el-date-picker
v-model="courseScheduleTable.searchParam.course_date" v-model="courseScheduleTable.searchParam.course_date"
type="date"
:placeholder="t('courseDatePlaceholder')" :placeholder="t('courseDatePlaceholder')"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
clearable
/> />
</el-form-item> </el-form-item>
<el-form-item :label="t('timeSlot')" prop="time_slot"> <!-- <el-form-item :label="t('coachId')" prop="coach_id">
<el-input
v-model="courseScheduleTable.searchParam.time_slot"
:placeholder="t('timeSlotPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('courseId')" prop="course_id">
<el-input
v-model="courseScheduleTable.searchParam.course_id"
:placeholder="t('courseIdPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('coachId')" prop="coach_id">
<el-input <el-input
v-model="courseScheduleTable.searchParam.coach_id" v-model="courseScheduleTable.searchParam.coach_id"
:placeholder="t('coachIdPlaceholder')" :placeholder="t('coachIdPlaceholder')"
/> />
</el-form-item> </el-form-item> -->
<el-form-item :label="t('participants')" prop="participants">
<el-input
v-model="courseScheduleTable.searchParam.participants"
:placeholder="t('participantsPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('studentIds')" prop="student_ids">
<el-input
v-model="courseScheduleTable.searchParam.student_ids"
:placeholder="t('studentIdsPlaceholder')"
/>
</el-form-item>
<el-form-item
:label="t('availableCapacity')"
prop="available_capacity"
>
<el-input
v-model="courseScheduleTable.searchParam.available_capacity"
:placeholder="t('availableCapacityPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-input
v-model="courseScheduleTable.searchParam.status"
:placeholder="t('statusPlaceholder')"
/>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="loadCourseScheduleList()">{{ <el-button type="primary" @click="loadCourseScheduleList()">{{
@ -108,14 +89,22 @@
:label="t('campusId')" :label="t('campusId')"
min-width="120" min-width="120"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> >
<template #default="{ row }">
{{ getCampusName(row.campus_id) }}
</template>
</el-table-column>
<el-table-column <el-table-column
prop="venue_id" prop="venue_id"
:label="t('venueId')" :label="t('venueId')"
min-width="120" min-width="120"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> >
<template #default="{ row }">
{{ row.venue ? row.venue.venue_name : '' }}
</template>
</el-table-column>
<el-table-column <el-table-column
prop="course_date" prop="course_date"
@ -136,15 +125,25 @@
:label="t('courseId')" :label="t('courseId')"
min-width="120" min-width="120"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> >
<template #default="{ row }">
{{ row.course ? row.course.course_name : '' }}
</template>
</el-table-column>
<el-table-column <el-table-column
prop="coach_id" prop="coach_id"
:label="t('coachId')" :label="t('coachId')"
min-width="120" min-width="120"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> >
<template #default="{ row }">
{{ row.coach ? row.coach.name : '' }}
</template>
</el-table-column>
<!--
<el-table-column <el-table-column
prop="participants" prop="participants"
:label="t('participants')" :label="t('participants')"
@ -164,14 +163,21 @@
:label="t('availableCapacity')" :label="t('availableCapacity')"
min-width="120" min-width="120"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> /> -->
<el-table-column <el-table-column
prop="status" prop="status"
:label="t('status')" :label="t('status')"
min-width="120" min-width="120"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> >
<template #default="{ row }">
<span v-if="row.status === 'pending'">{{ t('pending') }}</span>
<span v-if="row.status === 'upcoming'">{{ t('upcoming') }}</span>
<span v-if="row.status === 'ongoing'">{{ t('ongoing') }}</span>
<span v-if="row.status === 'completed'">{{ t('completed') }}</span>
</template>
</el-table-column>
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
@ -206,13 +212,15 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch, onMounted } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { import {
getCourseScheduleList, getCourseScheduleList,
deleteCourseSchedule, deleteCourseSchedule,
} from '@/app/api/course_schedule' } from '@/app/api/course_schedule'
import { getAllClassroomList } from '@/app/api/classroom'
import { getWithCampusList, getAllVenueList } from '@/app/api/venue'
import { img } from '@/utils/common' import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus' import { ElMessageBox, FormInstance } from 'element-plus'
import Edit from '@/app/views/course_schedule/components/course-schedule-edit.vue' import Edit from '@/app/views/course_schedule/components/course-schedule-edit.vue'
@ -229,14 +237,7 @@ let courseScheduleTable = reactive({
searchParam: { searchParam: {
campus_id: '', campus_id: '',
venue_id: '', venue_id: '',
course_date: '', course_date: ''
time_slot: '',
course_id: '',
coach_id: '',
participants: '',
student_ids: '',
available_capacity: '',
status: '',
}, },
}) })
@ -245,7 +246,58 @@ const searchFormRef = ref<FormInstance>()
// //
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
// //
const campusList = ref<any[]>([])
// -
const venueList = ref<any[]>([])
// -
const venueFilterList = ref<any[]>([])
// ID
const getCampusName = (id: string | number) => {
const campus = campusList.value.find(item => item.id === id)
return campus ? campus.campus_name : id
}
//
const loadCampusList = () => {
getWithCampusList({})
.then((res) => {
campusList.value = res.data || []
})
.catch(() => {})
}
// -
const loadVenueList = () => {
getAllVenueList({})
.then((res) => {
venueList.value = res.data || []
})
.catch(() => {})
}
// -
const loadVenuesByCampus = (campus_id?: string | number) => {
getAllVenueList({ campus_id })
.then((res) => {
venueFilterList.value = res.data || []
})
.catch(() => {})
}
//
watch(
() => courseScheduleTable.searchParam.campus_id,
(newValue) => {
courseScheduleTable.searchParam.venue_id = ''
if (newValue) {
loadVenuesByCampus(newValue)
} else {
venueFilterList.value = []
}
}
)
/** /**
* 获取课程安排列表 * 获取课程安排列表
@ -268,7 +320,13 @@ const loadCourseScheduleList = (page: number = 1) => {
courseScheduleTable.loading = false courseScheduleTable.loading = false
}) })
} }
loadCourseScheduleList()
//
onMounted(() => {
loadCampusList()
loadVenueList()
loadCourseScheduleList()
})
const editCourseScheduleDialog: Record<string, any> | null = ref(null) const editCourseScheduleDialog: Record<string, any> | null = ref(null)

271
admin/src/app/views/future_ontent/components/basketball-course-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editBasketballTeachingLibrary')
: t('addBasketballTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addBasketballTeachingLibrary,
editBasketballTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id
? editBasketballTeachingLibrary
: addBasketballTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

267
admin/src/app/views/future_ontent/components/ninja-teaching-edit.vue

@ -1,267 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id ? t('editNinjaTeachingLibrary') : t('addNinjaTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addNinjaTeachingLibrary,
editNinjaTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editNinjaTeachingLibrary : addNinjaTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

271
admin/src/app/views/future_ontent/components/physical-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editPhysicalTeachingLibrary')
: t('addPhysicalTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addPhysicalTeachingLibrary,
editPhysicalTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id
? editPhysicalTeachingLibrary
: addPhysicalTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

271
admin/src/app/views/future_ontent/components/security-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editSecurityTeachingLibrary')
: t('addSecurityTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addSecurityTeachingLibrary,
editSecurityTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id
? editSecurityTeachingLibrary
: addSecurityTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

269
admin/src/app/views/future_ontent/components/strengthen-course-teaching-edit.vue

@ -1,269 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editStrengthenTeachingLibrary')
: t('addStrengthenTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addStrengTeachingLibrary,
editStrengTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editStrengTeachingLibrary : addStrengTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

32
admin/src/app/views/future_ontent/future_ontent.vue

@ -127,7 +127,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ <el-button type="primary" link @click="editEvent(row)">{{
@ -145,6 +145,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -290,7 +295,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -311,6 +316,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -456,7 +466,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -477,6 +487,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -503,6 +518,10 @@
ref="BindingPersonnelDialog" ref="BindingPersonnelDialog"
@complete="loadLessonCourseTeachingList" @complete="loadLessonCourseTeachingList"
/> />
<binding-test-paper
ref="BindingTestPaperDialog"
@complete="loadLessonCourseTeachingList"
/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -521,6 +540,7 @@ import Edit from '@/app/views/future_ontent/components/lesson-course-teaching-ed
import EditJump from '@/app/views/future_ontent/components/Jump-lesson-library-edit.vue' import EditJump from '@/app/views/future_ontent/components/Jump-lesson-library-edit.vue'
import EditEn from '@/app/views/future_ontent/components/en-course-teaching-edit.vue' import EditEn from '@/app/views/future_ontent/components/en-course-teaching-edit.vue'
import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue' import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue'
import bindingTestPaper from '@/app/views/binding_test_paper/binding_test_paper.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
const pageName = route.meta.title const pageName = route.meta.title
@ -592,11 +612,17 @@ const editLessonCourseTeachingDialog: Record<string, any> | null = ref(null)
const editJumpLessonLibraryDialog: Record<string, any> | null = ref(null) const editJumpLessonLibraryDialog: Record<string, any> | null = ref(null)
const editEnTeachingLibraryDialog: Record<string, any> | null = ref(null) const editEnTeachingLibraryDialog: Record<string, any> | null = ref(null)
const BindingPersonnelDialog: Record<string, any> | null = ref(null) const BindingPersonnelDialog: Record<string, any> | null = ref(null)
const BindingTestPaperDialog: Record<string, any> | null = ref(null)
const addBindingPersonnel = (row) => { const addBindingPersonnel = (row) => {
BindingPersonnelDialog.value.setFormData(row) BindingPersonnelDialog.value.setFormData(row)
BindingPersonnelDialog.value.showDialog = true BindingPersonnelDialog.value.showDialog = true
} }
const addBindingTestPaper = (row) => {
BindingTestPaperDialog.value.setFormData(row)
BindingTestPaperDialog.value.showDialog = true
}
/** /**
* 获取课程教学大纲列表 * 获取课程教学大纲列表
*/ */

1952
admin/src/app/views/future_ontent/lesson_course_teaching.vue

File diff suppressed because it is too large

267
admin/src/app/views/instructional_material/components/ninja-teaching-edit.vue

@ -1,267 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id ? t('editNinjaTeachingLibrary') : t('addNinjaTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addNinjaTeachingLibrary,
editNinjaTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editNinjaTeachingLibrary : addNinjaTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

271
admin/src/app/views/instructional_material/components/physical-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editPhysicalTeachingLibrary')
: t('addPhysicalTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addPhysicalTeachingLibrary,
editPhysicalTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id
? editPhysicalTeachingLibrary
: addPhysicalTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

271
admin/src/app/views/instructional_material/components/security-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editSecurityTeachingLibrary')
: t('addSecurityTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addSecurityTeachingLibrary,
editSecurityTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id
? editSecurityTeachingLibrary
: addSecurityTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

269
admin/src/app/views/instructional_material/components/strengthen-course-teaching-edit.vue

@ -1,269 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editStrengthenTeachingLibrary')
: t('addStrengthenTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addStrengTeachingLibrary,
editStrengTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editStrengTeachingLibrary : addStrengTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

39
admin/src/app/views/instructional_material/instructional_material.vue

@ -125,7 +125,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -146,6 +146,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -276,7 +281,7 @@
<el-table-column <el-table-column
:label="t('status')" :label="t('status')"
min-width="180" min-width="250"
align="center" align="center"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
> >
@ -311,6 +316,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -456,7 +466,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -477,6 +487,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -626,7 +641,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -647,6 +662,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -673,6 +693,10 @@
ref="BindingPersonnelDialog" ref="BindingPersonnelDialog"
@complete="loadLessonCourseTeachingList" @complete="loadLessonCourseTeachingList"
/> />
<binding-test-paper
ref="BindingTestPaperDialog"
@complete="loadLessonCourseTeachingList"
/>
</div> </div>
</template> </template>
@ -697,6 +721,7 @@ import EditJump from '@/app/views/instructional_material/components/Jump-lesson-
import EditEn from '@/app/views/instructional_material/components/en-course-teaching-edit.vue' import EditEn from '@/app/views/instructional_material/components/en-course-teaching-edit.vue'
import EditBasketball from '@/app/views/instructional_material/components/basketball-course-teaching-edit.vue' import EditBasketball from '@/app/views/instructional_material/components/basketball-course-teaching-edit.vue'
import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue' import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue'
import bindingTestPaper from '@/app/views/binding_test_paper/binding_test_paper.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
const pageName = route.meta.title const pageName = route.meta.title
@ -782,11 +807,17 @@ const editBasketballTeachingLibraryDialog: Record<string, any> | null =
const editStrengthenTeachingLibraryDialog: Record<string, any> | null = const editStrengthenTeachingLibraryDialog: Record<string, any> | null =
ref(null) ref(null)
const BindingPersonnelDialog: Record<string, any> | null = ref(null) const BindingPersonnelDialog: Record<string, any> | null = ref(null)
const BindingTestPaperDialog: Record<string, any> | null = ref(null)
const addBindingPersonnel = (row) => { const addBindingPersonnel = (row) => {
BindingPersonnelDialog.value.setFormData(row) BindingPersonnelDialog.value.setFormData(row)
BindingPersonnelDialog.value.showDialog = true BindingPersonnelDialog.value.showDialog = true
} }
const addBindingTestPaper = (row) => {
BindingTestPaperDialog.value.setFormData(row)
BindingTestPaperDialog.value.showDialog = true
}
/** /**
* 获取热身动作库列表 * 获取热身动作库列表
*/ */

1952
admin/src/app/views/instructional_material/lesson_course_teaching.vue

File diff suppressed because it is too large

73
admin/src/app/views/lesson_course_teaching/lesson_course_teaching.vue

@ -127,7 +127,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ <el-button type="primary" link @click="editEvent(row)">{{
@ -137,14 +137,17 @@
type="primary" type="primary"
link link
@click="deleteEvent(row.id, 1)" @click="deleteEvent(row.id, 1)"
>{{ t('delete') }}</el-button >{{ t('delete') }}</el-button>
>
<el-button <el-button
type="primary" type="primary"
link link
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button>
> <el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -263,7 +266,7 @@
</el-table-column> </el-table-column>
<el-table-column <el-table-column
:label="t('type')" :label="t('type')"
min-width="180" min-width="250"
align="center" align="center"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
> >
@ -311,6 +314,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -442,7 +450,7 @@
<el-table-column <el-table-column
:label="t('status')" :label="t('status')"
min-width="180" min-width="250"
align="center" align="center"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
> >
@ -477,6 +485,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -599,7 +612,7 @@
</el-table-column> </el-table-column>
<el-table-column <el-table-column
:label="t('type')" :label="t('type')"
min-width="180" min-width="250"
align="center" align="center"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
> >
@ -647,6 +660,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -792,7 +810,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -813,6 +831,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -944,7 +967,7 @@
<el-table-column <el-table-column
:label="t('status')" :label="t('status')"
min-width="180" min-width="250"
align="center" align="center"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
> >
@ -979,6 +1002,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -1124,7 +1152,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -1145,6 +1173,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -1276,7 +1309,7 @@
<el-table-column <el-table-column
:label="t('status')" :label="t('status')"
min-width="180" min-width="250"
align="center" align="center"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
> >
@ -1311,6 +1344,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -1337,6 +1375,10 @@
ref="BindingPersonnelDialog" ref="BindingPersonnelDialog"
@complete="loadLessonCourseTeachingList" @complete="loadLessonCourseTeachingList"
/> />
<binding-test-paper
ref="BindingTestPaperDialog"
@complete="loadLessonCourseTeachingList"
/>
</div> </div>
</template> </template>
@ -1367,6 +1409,7 @@ import EditNinja from '@/app/views/lesson_course_teaching/components/ninja-teach
import EditSecurity from '@/app/views/lesson_course_teaching/components/security-teaching-edit.vue' import EditSecurity from '@/app/views/lesson_course_teaching/components/security-teaching-edit.vue'
import EditPhysical from '@/app/views/lesson_course_teaching/components/physical-teaching-edit.vue' import EditPhysical from '@/app/views/lesson_course_teaching/components/physical-teaching-edit.vue'
import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue' import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue'
import bindingTestPaper from '@/app/views/binding_test_paper/binding_test_paper.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
const pageName = route.meta.title const pageName = route.meta.title
@ -1506,12 +1549,18 @@ const editNinjaTeachingLibraryDialog: Record<string, any> | null = ref(null)
const editSecurityTeachingLibraryDialog: Record<string, any> | null = ref(null) const editSecurityTeachingLibraryDialog: Record<string, any> | null = ref(null)
const editPhysicalTeachingLibraryDialog: Record<string, any> | null = ref(null) const editPhysicalTeachingLibraryDialog: Record<string, any> | null = ref(null)
const BindingPersonnelDialog: Record<string, any> | null = ref(null) const BindingPersonnelDialog: Record<string, any> | null = ref(null)
const BindingTestPaperDialog: Record<string, any> | null = ref(null)
const addBindingPersonnel = (row) => { const addBindingPersonnel = (row) => {
BindingPersonnelDialog.value.setFormData(row) BindingPersonnelDialog.value.setFormData(row)
BindingPersonnelDialog.value.showDialog = true BindingPersonnelDialog.value.showDialog = true
} }
const addBindingTestPaper = (row) => {
BindingTestPaperDialog.value.setFormData(row)
BindingTestPaperDialog.value.showDialog = true
}
/** /**
* 获取课程教学大纲列表 * 获取课程教学大纲列表
*/ */

32
admin/src/app/views/order_table/order_table.vue

@ -116,7 +116,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import QrcodeVue from '@/app/views/order_table/components/qrcode.vue' // import QrcodeVue from '@/app/views/order_table/components/qrcode.vue'
import { reactive, ref, watch, onBeforeUnmount } from 'vue' import { reactive, ref, watch, onBeforeUnmount } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
@ -141,7 +141,7 @@
const route = useRoute() const route = useRoute()
const pageName = route.meta.title const pageName = route.meta.title
let orderTableTable = reactive({ let orderTableTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
@ -155,12 +155,12 @@
}, },
}) })
const showDialog = ref(false) const showDialog = ref(false)
const qrCodeUrl = ref('') const qrCodeUrl = ref('')
const payId = ref('') const payId = ref('')
let checkTimer: ReturnType<typeof setInterval> | null = null let checkTimer: ReturnType<typeof setInterval> | null = null
const startPayment = async (order_id : number) => { const startPayment = async (order_id : number) => {
orderPay({ orderPay({
@ -178,7 +178,7 @@
}) })
} }
const startCheckingStatus = () => { const startCheckingStatus = () => {
if (checkTimer) clearInterval(checkTimer) if (checkTimer) clearInterval(checkTimer)
checkTimer = setInterval(async () => { checkTimer = setInterval(async () => {
@ -193,17 +193,17 @@
} }
}) })
.catch(() => { .catch(() => {
}) })
}, 3000) }, 3000)
} }
const handleClose = () => { const handleClose = () => {
if (checkTimer) clearInterval(checkTimer) if (checkTimer) clearInterval(checkTimer)
} }
const handlePaymentSuccess = () => { const handlePaymentSuccess = () => {
// //
loadOrderTableList(); loadOrderTableList();
@ -332,8 +332,8 @@
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
.qr-box { .qr-box {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -345,7 +345,7 @@
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
.qr-img { .qr-img {
width: 200px; width: 200px;
height: 200px; height: 200px;
@ -353,11 +353,11 @@
object-fit: contain; object-fit: contain;
border: 1px solid #e5e5e5; border: 1px solid #e5e5e5;
} }
.pay-id { .pay-id {
margin-top: 14px; margin-top: 14px;
font-size: 13px; font-size: 13px;
color: #666; color: #666;
word-break: break-all; word-break: break-all;
} }
</style> </style>

271
admin/src/app/views/physical_testing/components/ninja-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id ? t('editNinjaTeachingLibrary') : t('addNinjaTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addNinjaTeachingLibrary,
editNinjaTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
import {
addPublicLibraryList,
editPublicLibraryList,
} from '@/app/api/future_ontent'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
table_type: 21,
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editPublicLibraryList : addPublicLibraryList
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
data.table_type = 21
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

271
admin/src/app/views/physical_testing/components/physical-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editPhysicalTeachingLibrary')
: t('addPhysicalTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addPhysicalTeachingLibrary,
editPhysicalTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id
? editPhysicalTeachingLibrary
: addPhysicalTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

273
admin/src/app/views/physical_testing/components/security-teaching-edit.vue

@ -1,273 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editSecurityTeachingLibrary')
: t('addSecurityTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addSecurityTeachingLibrary,
editSecurityTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
import {
addPublicLibraryList,
editPublicLibraryList,
} from '@/app/api/future_ontent'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
table_type: 22,
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editPublicLibraryList : addPublicLibraryList
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
data.table_type = 22
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

273
admin/src/app/views/physical_testing/components/strengthen-course-teaching-edit.vue

@ -1,273 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editStrengthenTeachingLibrary')
: t('addStrengthenTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addStrengTeachingLibrary,
editStrengTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
import {
addPublicLibraryList,
editPublicLibraryList,
} from '@/app/api/future_ontent'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
table_type: 20,
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editPublicLibraryList : addPublicLibraryList
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
data.table_type = 20
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

40
admin/src/app/views/physical_testing/physical_testing.vue

@ -127,7 +127,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ <el-button type="primary" link @click="editEvent(row)">{{
@ -145,6 +145,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -290,7 +295,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -311,6 +316,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -456,7 +466,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -477,6 +487,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -626,7 +641,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -647,6 +662,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -673,6 +693,10 @@
ref="BindingPersonnelDialog" ref="BindingPersonnelDialog"
@complete="loadLessonCourseTeachingList" @complete="loadLessonCourseTeachingList"
/> />
<binding-test-paper
ref="BindingTestPaperDialog"
@complete="loadLessonCourseTeachingList"
/>
</div> </div>
</template> </template>
@ -698,6 +722,7 @@ import Edit from '@/app/views/physical_testing/components/lesson-course-teaching
import EditJump from '@/app/views/physical_testing/components/Jump-lesson-library-edit.vue' import EditJump from '@/app/views/physical_testing/components/Jump-lesson-library-edit.vue'
import EditEn from '@/app/views/physical_testing/components/en-course-teaching-edit.vue' import EditEn from '@/app/views/physical_testing/components/en-course-teaching-edit.vue'
import EditBasketball from '@/app/views/physical_testing/components/basketball-course-teaching-edit.vue' import EditBasketball from '@/app/views/physical_testing/components/basketball-course-teaching-edit.vue'
import bindingTestPaper from '@/app/views/binding_test_paper/binding_test_paper.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue' import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue'
const route = useRoute() const route = useRoute()
@ -786,11 +811,16 @@ const editEnTeachingLibraryDialog: Record<string, any> | null = ref(null)
const editBasketballTeachingLibraryDialog: Record<string, any> | null = const editBasketballTeachingLibraryDialog: Record<string, any> | null =
ref(null) ref(null)
const BindingPersonnelDialog: Record<string, any> | null = ref(null) const BindingPersonnelDialog: Record<string, any> | null = ref(null)
const BindingTestPaperDialog: Record<string, any> | null = ref(null)
const addBindingPersonnel = (row) => { const addBindingPersonnel = (row) => {
BindingPersonnelDialog.value.setFormData(row) BindingPersonnelDialog.value.setFormData(row)
BindingPersonnelDialog.value.showDialog = true BindingPersonnelDialog.value.showDialog = true
} }
const addBindingTestPaper = (row) => {
BindingTestPaperDialog.value.setFormData(row)
BindingTestPaperDialog.value.showDialog = true
}
/** /**
* 获取课程教学大纲列表 * 获取课程教学大纲列表
*/ */

1736
admin/src/app/views/physical_testing/professional_skills.vue

File diff suppressed because it is too large

271
admin/src/app/views/professional_skills/components/physical-teaching-edit.vue

@ -1,271 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.id
? t('editPhysicalTeachingLibrary')
: t('addPhysicalTeachingLibrary')
"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('image')" prop="image">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select
class="input-width"
v-model="formData.type"
clearable
:placeholder="t('typePlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<editor v-model="formData.content" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group
v-model="formData.status"
:placeholder="t('statusPlaceholder')"
>
<el-radio
v-for="(item, index) in statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('userPermission')" prop="user_permission">-->
<!-- <el-checkbox-group v-model="formData.user_permission" :placeholder="t('userPermissionPlaceholder')">-->
<!-- <el-checkbox-->
<!-- v-for="(item, index) in userPermissionList"-->
<!-- :key="index"-->
<!-- :label="item['sys_user_id']">-->
<!-- {{ item['name'] }}-->
<!-- </el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addPhysicalTeachingLibrary,
editPhysicalTeachingLibrary,
getLessonCourseTeachingInfo,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
image: '',
type: '',
content: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
],
image: [
{ required: true, message: t('imagePlaceholder'), trigger: 'blur' },
],
type: [{ required: true, message: t('typePlaceholder'), trigger: 'blur' }],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id
? editPhysicalTeachingLibrary
: addPhysicalTeachingLibrary
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
watch(
() => typeList.value,
() => {
formData.type = typeList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
const userPermissionList = ref([] as any[])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getLessonCourseTeachingInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (
value &&
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

789
admin/src/app/views/professional_skills/future_ontent.vue

@ -1,789 +0,0 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-tabs v-model="active">
<el-tab-pane label="训练内容" name="CourseSyllabus">
<div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent">
{{ t('addLessonCourseTeaching') }}
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="lessonCourseTeachingTable.searchParam"
ref="searchFormRef"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="lessonCourseTeachingTable.searchParam.title"
:placeholder="t('titlePlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select
class="w-[280px]"
v-model="lessonCourseTeachingTable.searchParam.status"
clearable
:placeholder="t('statusPlaceholder')"
>
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker
v-model="lessonCourseTeachingTable.searchParam.create_time"
type="datetimerange"
format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
</el-form-item>
<el-form-item :label="t('updateTime')" prop="update_time">
<el-date-picker
v-model="lessonCourseTeachingTable.searchParam.update_time"
type="datetimerange"
format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="loadLessonCourseTeachingList()"
>{{ t('search') }}</el-button
>
<el-button @click="resetForm(searchFormRef, 13)">{{
t('reset')
}}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="lessonCourseTeachingTable.data"
size="large"
v-loading="lessonCourseTeachingTable.loading"
>
<template #empty>
<span>{{
!lessonCourseTeachingTable.loading ? t('emptyData') : ''
}}</span>
</template>
<el-table-column
prop="title"
:label="t('title')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column :label="t('image')" width="100" align="left">
<template #default="{ row }">
<el-avatar v-if="row.image" :src="img(row.image)" />
<el-avatar v-else icon="UserFilled" />
</template>
</el-table-column>
<el-table-column
:label="t('type')"
min-width="180"
align="center"
:show-overflow-tooltip="true"
>
<template #default="{ row }">
<div v-for="(item, index) in typeList">
<div v-if="item.value == row.type">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column
:label="t('status')"
min-width="180"
align="center"
:show-overflow-tooltip="true"
>
<template #default="{ row }">
<div v-for="(item, index) in statusList">
<div v-if="item.value == row.status">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
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, 13)"
>{{ t('delete') }}</el-button
>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="lessonCourseTeachingTable.page"
v-model:page-size="lessonCourseTeachingTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="lessonCourseTeachingTable.total"
@size-change="loadLessonCourseTeachingList()"
@current-change="loadLessonCourseTeachingList"
/>
</div>
</div>
<edit
ref="editLessonCourseTeachingDialog"
@complete="loadLessonCourseTeachingList"
/>
</el-tab-pane>
<el-tab-pane label="训练视频" name="JumpLessonLibrary">
<div class="flex justify-between items-center">
<el-button type="primary" @click="addJumpLessonLibrary">
{{ t('addJumpLessonLibrary') }}
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="JumpLessonLibraryTable.searchParam"
ref="searchFormRef"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="JumpLessonLibraryTable.searchParam.title"
:placeholder="t('titlePlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select
class="w-[280px]"
v-model="JumpLessonLibraryTable.searchParam.status"
clearable
:placeholder="t('statusPlaceholder')"
>
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker
v-model="JumpLessonLibraryTable.searchParam.create_time"
type="datetimerange"
format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
</el-form-item>
<el-form-item :label="t('updateTime')" prop="update_time">
<el-date-picker
v-model="JumpLessonLibraryTable.searchParam.update_time"
type="datetimerange"
format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="loadJumpLessonLibraryList()"
>{{ t('search') }}</el-button
>
<el-button @click="resetForm(searchFormRef, 14)">{{
t('reset')
}}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="JumpLessonLibraryTable.data"
size="large"
v-loading="JumpLessonLibraryTable.loading"
>
<template #empty>
<span>{{
!JumpLessonLibraryTable.loading ? t('emptyData') : ''
}}</span>
</template>
<el-table-column
prop="title"
:label="t('title')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column :label="t('image')" width="100" align="left">
<template #default="{ row }">
<el-avatar v-if="row.image" :src="img(row.image)" />
<el-avatar v-else icon="UserFilled" />
</template>
</el-table-column>
<el-table-column
:label="t('type')"
min-width="180"
align="center"
:show-overflow-tooltip="true"
>
<template #default="{ row }">
<div v-for="(item, index) in typeList">
<div v-if="item.value == row.type">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column
:label="t('status')"
min-width="180"
align="center"
:show-overflow-tooltip="true"
>
<template #default="{ row }">
<div v-for="(item, index) in statusList">
<div v-if="item.value == row.status">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button
type="primary"
link
@click="editJumpLessonLibrary(row)"
>{{ t('edit') }}</el-button
>
<el-button
type="primary"
link
@click="deleteEvent(row.id, 14)"
>{{ t('delete') }}</el-button
>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="JumpLessonLibraryTable.page"
v-model:page-size="JumpLessonLibraryTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="JumpLessonLibraryTable.total"
@size-change="loadJumpLessonLibraryList()"
@current-change="loadJumpLessonLibraryList"
/>
</div>
</div>
<edit-jump
ref="editJumpLessonLibraryDialog"
@complete="loadJumpLessonLibraryList"
/>
</el-tab-pane>
<el-tab-pane label="课后作业" name="EnTeachingLibrary">
<div class="flex justify-between items-center">
<el-button type="primary" @click="addEnTeachingLibrary">
{{ t('addEnTeachingLibrary') }}
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="EnTeachingLibraryTable.searchParam"
ref="searchFormRef"
>
<el-form-item :label="t('title')" prop="title">
<el-input
v-model="EnTeachingLibraryTable.searchParam.title"
:placeholder="t('titlePlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select
class="w-[280px]"
v-model="EnTeachingLibraryTable.searchParam.status"
clearable
:placeholder="t('statusPlaceholder')"
>
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker
v-model="EnTeachingLibraryTable.searchParam.create_time"
type="datetimerange"
format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
</el-form-item>
<el-form-item :label="t('updateTime')" prop="update_time">
<el-date-picker
v-model="EnTeachingLibraryTable.searchParam.update_time"
type="datetimerange"
format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="loadEnTeachingLibraryList()"
>{{ t('search') }}</el-button
>
<el-button @click="resetForm(searchFormRef, 15)">{{
t('reset')
}}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="EnTeachingLibraryTable.data"
size="large"
v-loading="EnTeachingLibraryTable.loading"
>
<template #empty>
<span>{{
!EnTeachingLibraryTable.loading ? t('emptyData') : ''
}}</span>
</template>
<el-table-column
prop="title"
:label="t('title')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column :label="t('image')" width="100" align="left">
<template #default="{ row }">
<el-avatar v-if="row.image" :src="img(row.image)" />
<el-avatar v-else icon="UserFilled" />
</template>
</el-table-column>
<el-table-column
:label="t('type')"
min-width="180"
align="center"
:show-overflow-tooltip="true"
>
<template #default="{ row }">
<div v-for="(item, index) in typeList">
<div v-if="item.value == row.type">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column
:label="t('status')"
min-width="180"
align="center"
:show-overflow-tooltip="true"
>
<template #default="{ row }">
<div v-for="(item, index) in statusList">
<div v-if="item.value == row.status">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button
type="primary"
link
@click="editEnTeachingLibrary(row)"
>{{ t('edit') }}</el-button
>
<el-button
type="primary"
link
@click="deleteEvent(row.id, 15)"
>{{ t('delete') }}</el-button
>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="EnTeachingLibraryTable.page"
v-model:page-size="EnTeachingLibraryTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="EnTeachingLibraryTable.total"
@size-change="loadEnTeachingLibraryList()"
@current-change="loadEnTeachingLibraryList"
/>
</div>
</div>
<edit-en
ref="editEnTeachingLibraryDialog"
@complete="loadEnTeachingLibraryList"
/>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
addPublicLibraryList editPublicLibraryList
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import {
getLessonCourseTeachingList,
deleteLessonCourseTeaching,
getWithPersonnelDataList,
} from '@/app/api/lesson_course_teaching'
import { getPublicLibraryList } from '@/app/api/future_ontent'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import Edit from '@/app/views/future_ontent/components/lesson-course-teaching-edit.vue'
import EditJump from '@/app/views/future_ontent/components/Jump-lesson-library-edit.vue'
import EditEn from '@/app/views/future_ontent/components/en-course-teaching-edit.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
const active = 'CourseSyllabus'
let lessonCourseTeachingTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
title: '',
status: '',
create_time: [],
update_time: [],
table_type: 13,
},
})
let JumpLessonLibraryTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
title: '',
status: '',
create_time: [],
update_time: [],
table_type: 14,
},
})
let EnTeachingLibraryTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
title: '',
status: '',
create_time: [],
update_time: [],
table_type: 15,
},
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const typeList = ref([] as any[])
const typeDictList = async () => {
typeList.value = await (await useDictionary('material_type')).data.dictionary
}
typeDictList()
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('course_status')
).data.dictionary
}
statusDictList()
const editLessonCourseTeachingDialog: Record<string, any> | null = ref(null)
const editJumpLessonLibraryDialog: Record<string, any> | null = ref(null)
const editEnTeachingLibraryDialog: Record<string, any> | null = ref(null)
/**
* 获取课程教学大纲列表
*/
const loadLessonCourseTeachingList = (page: number = 1) => {
lessonCourseTeachingTable.loading = true
lessonCourseTeachingTable.page = page
getPublicLibraryList({
page: lessonCourseTeachingTable.page,
limit: lessonCourseTeachingTable.limit,
...lessonCourseTeachingTable.searchParam,
})
.then((res) => {
lessonCourseTeachingTable.loading = false
lessonCourseTeachingTable.data = res.data.data
lessonCourseTeachingTable.total = res.data.total
})
.catch(() => {
lessonCourseTeachingTable.loading = false
})
}
loadLessonCourseTeachingList()
/**
* 添加课程教学大纲
*/
const addEvent = () => {
editLessonCourseTeachingDialog.value.setFormData()
editLessonCourseTeachingDialog.value.showDialog = true
}
/**
* 编辑课程教学大纲
* @param data
*/
const editEvent = (data: any) => {
editLessonCourseTeachingDialog.value.setFormData(data)
editLessonCourseTeachingDialog.value.showDialog = true
}
/**
* 获取跳绳教案库列表
*/
const loadJumpLessonLibraryList = (page: number = 1) => {
JumpLessonLibraryTable.loading = true
JumpLessonLibraryTable.page = page
getPublicLibraryList({
page: JumpLessonLibraryTable.page,
limit: JumpLessonLibraryTable.limit,
...JumpLessonLibraryTable.searchParam,
})
.then((res) => {
JumpLessonLibraryTable.loading = false
JumpLessonLibraryTable.data = res.data.data
JumpLessonLibraryTable.total = res.data.total
})
.catch(() => {
JumpLessonLibraryTable.loading = false
})
}
loadJumpLessonLibraryList()
/**
* 添加跳绳教案库
*/
const addJumpLessonLibrary = () => {
editJumpLessonLibraryDialog.value.setFormData()
editJumpLessonLibraryDialog.value.showDialog = true
}
/**
* 编辑跳绳教案库
* @param data
*/
const editJumpLessonLibrary = (data: any) => {
editJumpLessonLibraryDialog.value.setFormData(data)
editJumpLessonLibraryDialog.value.showDialog = true
}
/**
* 获取增高教案库列表
*/
const loadEnTeachingLibraryList = (page: number = 1) => {
EnTeachingLibraryTable.loading = true
EnTeachingLibraryTable.page = page
getPublicLibraryList({
page: EnTeachingLibraryTable.page,
limit: EnTeachingLibraryTable.limit,
...EnTeachingLibraryTable.searchParam,
})
.then((res) => {
EnTeachingLibraryTable.loading = false
EnTeachingLibraryTable.data = res.data.data
EnTeachingLibraryTable.total = res.data.total
})
.catch(() => {
EnTeachingLibraryTable.loading = false
})
}
loadEnTeachingLibraryList()
/**
* 添加增高教案库
*/
const addEnTeachingLibrary = () => {
editEnTeachingLibraryDialog.value.setFormData()
editEnTeachingLibraryDialog.value.showDialog = true
}
/**
* 编辑增高教案库
* @param data
*/
const editEnTeachingLibrary = (data: any) => {
editEnTeachingLibraryDialog.value.setFormData(data)
editEnTeachingLibraryDialog.value.showDialog = true
}
/**
* 删除教研管理
*/
const deleteEvent = (id: number, type: number) => {
ElMessageBox.confirm(t('lessonCourseTeachingDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteLessonCourseTeaching(id)
.then(() => {
if (type === 13) {
loadLessonCourseTeachingList()
} else if (type === 14) {
loadJumpLessonLibraryList()
} else if (type === 15) {
loadEnTeachingLibraryList()
}
})
.catch(() => {})
})
}
const userPermissionList = ref([])
const setUserPermissionList = async () => {
userPermissionList.value = await (await getWithPersonnelDataList({})).data
}
setUserPermissionList()
const resetForm = (formEl: FormInstance | undefined, type: number) => {
if (!formEl) return
formEl.resetFields()
if (type === 13) {
lessonCourseTeachingTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
title: '',
status: '',
create_time: [],
update_time: [],
table_type: 13,
},
})
loadLessonCourseTeachingList()
} else if (type === 14) {
JumpLessonLibraryTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
title: '',
status: '',
create_time: [],
update_time: [],
table_type: 14,
},
})
loadJumpLessonLibraryList()
} else if (type === 15) {
EnTeachingLibraryTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
title: '',
status: '',
create_time: [],
update_time: [],
table_type: 15,
},
})
loadEnTeachingLibraryList()
}
}
</script>
<style lang="scss" scoped>
/* 多行超出隐藏 */
.multi-hidden {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>

62
admin/src/app/views/professional_skills/professional_skills.vue

@ -127,7 +127,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ <el-button type="primary" link @click="editEvent(row)">{{
@ -145,6 +145,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -290,7 +295,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -311,6 +316,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -456,7 +466,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -477,6 +487,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -626,7 +641,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -647,6 +662,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -792,7 +812,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -813,6 +833,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -958,7 +983,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -979,6 +1004,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -1124,7 +1154,7 @@
<el-table-column <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="180" min-width="250"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
@ -1145,6 +1175,11 @@
@click="addBindingPersonnel(row)" @click="addBindingPersonnel(row)"
>{{ t('addBindingPersonnel') }}</el-button >{{ t('addBindingPersonnel') }}</el-button
> >
<el-button
type="primary"
link
@click="addBindingTestPaper(row)"
>{{ t('addBindingTestPaper') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -1171,6 +1206,10 @@
ref="BindingPersonnelDialog" ref="BindingPersonnelDialog"
@complete="loadLessonCourseTeachingList" @complete="loadLessonCourseTeachingList"
/> />
<binding-test-paper
ref="BindingTestPaperDialog"
@complete="loadLessonCourseTeachingList"
/>
</div> </div>
</template> </template>
@ -1199,7 +1238,7 @@ import EditBasketball from '@/app/views/professional_skills/components/basketbal
import EditStrengthen from '@/app/views/professional_skills/components/strengthen-course-teaching-edit.vue' import EditStrengthen from '@/app/views/professional_skills/components/strengthen-course-teaching-edit.vue'
import EditNinja from '@/app/views/professional_skills/components/ninja-teaching-edit.vue' import EditNinja from '@/app/views/professional_skills/components/ninja-teaching-edit.vue'
import EditSecurity from '@/app/views/professional_skills/components/security-teaching-edit.vue' import EditSecurity from '@/app/views/professional_skills/components/security-teaching-edit.vue'
import EditPhysical from '@/app/views/professional_skills/components/physical-teaching-edit.vue' import bindingTestPaper from '@/app/views/binding_test_paper/binding_test_paper.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue' import bindingPersonnel from '@/app/views/binding_personnel/binding_personnel.vue'
const route = useRoute() const route = useRoute()
@ -1334,11 +1373,16 @@ const editStrengthenTeachingLibraryDialog: Record<string, any> | null =
const editNinjaTeachingLibraryDialog: Record<string, any> | null = ref(null) const editNinjaTeachingLibraryDialog: Record<string, any> | null = ref(null)
const editSecurityTeachingLibraryDialog: Record<string, any> | null = ref(null) const editSecurityTeachingLibraryDialog: Record<string, any> | null = ref(null)
const BindingPersonnelDialog: Record<string, any> | null = ref(null) const BindingPersonnelDialog: Record<string, any> | null = ref(null)
const BindingTestPaperDialog: Record<string, any> | null = ref(null)
const addBindingPersonnel = (row) => { const addBindingPersonnel = (row) => {
BindingPersonnelDialog.value.setFormData(row) BindingPersonnelDialog.value.setFormData(row)
BindingPersonnelDialog.value.showDialog = true BindingPersonnelDialog.value.showDialog = true
} }
const addBindingTestPaper = (row) => {
BindingTestPaperDialog.value.setFormData(row)
BindingTestPaperDialog.value.showDialog = true
}
/** /**
* 获取课程教学大纲列表 * 获取课程教学大纲列表
*/ */

415
admin/src/app/views/timetables/components/schedule-add.vue

@ -25,7 +25,7 @@
<el-select <el-select
v-model="form.venue_id" v-model="form.venue_id"
placeholder="请选择场地" placeholder="请选择场地"
@change="calculateAvailableCapacity" @change="handleVenueChange"
> >
<el-option <el-option
v-for="item in venueList" v-for="item in venueList"
@ -51,7 +51,7 @@
<el-select <el-select
v-model="form.time_slot" v-model="form.time_slot"
placeholder="请选择上课时段" placeholder="请选择上课时段"
@change="calculateAvailableCapacity" @change="handleTimeSlotChange"
> >
<el-option <el-option
v-for="(timeSlot, index) in timeSlotOptions" v-for="(timeSlot, index) in timeSlotOptions"
@ -62,10 +62,6 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="剩余空位" v-if="form.venue_id && form.time_slot">
<span>{{ availableCapacity }}</span>
</el-form-item>
<el-form-item label="上课类型" prop="course_type"> <el-form-item label="上课类型" prop="course_type">
<el-radio-group v-model="form.course_type" @change="handleCourseTypeChange"> <el-radio-group v-model="form.course_type" @change="handleCourseTypeChange">
<el-radio label="class">班级</el-radio> <el-radio label="class">班级</el-radio>
@ -74,16 +70,16 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="班级" prop="class_ids" v-if="form.course_type === 'class'"> <el-form-item label="位置选择" v-if="form.venue_id">
<el-checkbox-group v-model="form.class_ids" @change="handleClassChange"> <SeatSelector
<el-checkbox :venueId="form.venue_id"
v-for="item in classList" :capacity="maxSeatSelections"
:key="item.id" v-model:selectedSeats="form.selectedSeats"
:label="item.id" :selected-seat-ids="selectedStudentSeats"
> :max-selections="maxSeatSelections"
{{ item.class_name }} @change="handleSeatSelectionChange"
</el-checkbox> @update:selected-seat-ids="updateSelectedStudentSeats"
</el-checkbox-group> />
</el-form-item> </el-form-item>
<el-form-item label="学员" prop="student_ids" v-if="form.course_type === 'student' || form.course_type === 'trial'"> <el-form-item label="学员" prop="student_ids" v-if="form.course_type === 'student' || form.course_type === 'trial'">
@ -105,6 +101,28 @@
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="班级学员" prop="student_ids" v-if="form.course_type === 'class'">
<el-select v-model="form.class_id" placeholder="请选择班级">
<el-option v-for="item in classList" :key="item.id" :label="item.class_name" :value="item.id" />
</el-select>
<div class="class-student-list">
<div class="class-student-grid">
<div
v-for="item in filteredClassStudentList"
:key="item.id"
class="class-student-item"
>
<el-checkbox
:model-value="item.checked"
@update:model-value="(val) => handleClassStudentSelectionChange(item.id, val)"
>
{{ item.name }}
</el-checkbox>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="课程名称" prop="course_name"> <el-form-item label="课程名称" prop="course_name">
<el-input v-model="form.course_name" placeholder="请输入课程名称" /> <el-input v-model="form.course_name" placeholder="请输入课程名称" />
</el-form-item> </el-form-item>
@ -129,11 +147,11 @@
<script setup> <script setup>
import { ref, defineProps, defineEmits, watch } from 'vue' import { ref, defineProps, defineEmits, watch } from 'vue'
import {getAllVenueList, getVenueList} from '@/app/api/venue' import {getAllVenueList} from '@/app/api/venue'
import { getClassroomList, getWithPersonnelList } from '@/app/api/classroom' import { getAllClassroomList, getClassroompeople, getWithPersonnelList, getClassroompeopleCount } from '@/app/api/classroom'
import { addCourseSchedule } from '@/app/api/course_schedule' import { addCourseSchedule } from '@/app/api/course_schedule'
import { getTimetables } from '@/app/api/course_schedule'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import SeatSelector from './seat-selector.vue'
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -155,10 +173,17 @@ const venueList = ref([])
const classList = ref([]) const classList = ref([])
const studentList = ref([]) const studentList = ref([])
const coachList = 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 timeSlotOptions = ref([])
const availableCapacity = ref(0) const availableCapacity = ref(0)
const studentSearchKeyword = ref('') const studentSearchKeyword = ref('')
const filteredStudentList = ref([]) const filteredStudentList = ref([])
const occupiedSeats = ref([])
const participants = ref([]) // getClassroompeopleCount
const selectedStudentSeats = ref([]) //
const maxSeatSelections = ref(30) //
const classStudentList = ref([]) //
const classStudentSearchKeyword = ref('') //
const filteredClassStudentList = ref([]) //
// visible // visible
watch(() => props.visible, (newVal) => { watch(() => props.visible, (newVal) => {
@ -173,6 +198,10 @@ watch(dialogVisible, (newVal) => {
loadCoachList() loadCoachList()
if (form.value.campus_id) { if (form.value.campus_id) {
handleCampusChange() handleCampusChange()
//
if (form.value.course_type === 'class') {
handleCourseTypeChange()
}
} }
} }
}) })
@ -186,7 +215,8 @@ const form = ref({
class_ids: [], class_ids: [],
student_ids: [], student_ids: [],
course_name: '', course_name: '',
coach_id: '' coach_id: '',
selectedSeats: []
}) })
const rules = { const rules = {
@ -216,9 +246,9 @@ const handleCampusChange = async () => {
// //
try { try {
const response = await getClassroomList({ campus_id: form.value.campus_id }) const response = await getAllClassroomList({ campus_id: form.value.campus_id })
if (response.data && response.data.list) { if (response.data && response.data) {
classList.value = response.data.list classList.value = response.data
} }
} catch (error) { } catch (error) {
console.error('获取班级列表失败:', error) console.error('获取班级列表失败:', error)
@ -227,76 +257,231 @@ const handleCampusChange = async () => {
// //
form.value.venue_id = '' form.value.venue_id = ''
form.value.class_ids = [] form.value.class_ids = []
form.value.time_slot = ''
timeSlotOptions.value = []
availableCapacity.value = 0 availableCapacity.value = 0
} occupiedSeats.value = []
// //
const calculateAvailableCapacity = async () => { if (form.value.course_type === 'class') {
if (!form.value.venue_id || !form.value.time_slot) { handleCourseTypeChange()
availableCapacity.value = 0
return
} }
}
try { //
// const generateTimeSlots = (venueInfo) => {
const venueInfo = venueList.value.find(item => item.id === form.value.venue_id) if (!venueInfo) return []
if (venueInfo) {
// const timeSlots = []
availableCapacity.value = venueInfo.capacity || 0
// time_range_type
// switch (venueInfo.time_range_type) {
if (form.value.course_date) { case 'fixed':
const params = { // fixed_time_ranges
venue_id: form.value.venue_id, try {
course_date: form.value.course_date, const fixedRanges = JSON.parse(venueInfo.fixed_time_ranges || '[]')
time_slot: form.value.time_slot fixedRanges.forEach(range => {
timeSlots.push(`${range.start_time}-${range.end_time}`)
})
} catch (error) {
console.error('解析固定时间段失败:', error)
}
break
case 'all':
// 12:30-14:00
// 812:3030
for (let hour = 8; hour <= 12; hour++) {
for (let minute = 0; minute < 60; minute += 60) {
if (hour === 12 && minute === 30) continue // 12:30
const startHour = hour
const startMinute = minute
const endHour = minute === 30 ? hour + 1 : hour
const endMinute = minute === 30 ? 0 : 30
const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}`
const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}`
timeSlots.push(`${startTime}-${endTime}`)
}
}
// 14:0022:0030
for (let hour = 14; hour < 22; hour++) {
for (let minute = 0; minute < 60; minute += 60) {
const startHour = hour
const startMinute = minute
const endHour = minute === 30 ? hour + 1 : hour
const endMinute = minute === 30 ? 0 : 30
const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}`
const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}`
timeSlots.push(`${startTime}-${endTime}`)
} }
}
break
case 'range':
// 使10
if (venueInfo.time_range_start && venueInfo.time_range_end) {
const startTimeParts = venueInfo.time_range_start.split(':')
const endTimeParts = venueInfo.time_range_end.split(':')
const response = await getTimetables(params) if (startTimeParts.length === 2 && endTimeParts.length === 2) {
if (response.data && response.data.length > 0) { const startHour = parseInt(startTimeParts[0])
// const startMinute = parseInt(startTimeParts[1])
const matchingDay = response.data.find(day => const endHour = parseInt(endTimeParts[0])
day.date.includes(form.value.course_date) const endMinute = parseInt(endTimeParts[1])
)
//
const startTotalMinutes = startHour * 60 + startMinute
const endTotalMinutes = endHour * 60 + endMinute
if (matchingDay) { // 60
const matchingTimeSlot = matchingDay.timeSlots.find(slot => for (let minutes = startTotalMinutes; minutes < endTotalMinutes; minutes += 60) {
slot.timeRange === form.value.time_slot const startHour = Math.floor(minutes / 60)
) const startMinute = minutes % 60
const endHour = Math.floor((minutes + 60) / 60)
const endMinute = (minutes + 60) % 60
if (matchingTimeSlot && matchingTimeSlot.course) { const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}`
// const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}`
availableCapacity.value = matchingTimeSlot.course.hasnumber
} timeSlots.push(`${startTime}-${endTime}`)
} }
} }
} }
} break
} catch (error) {
console.error('计算可用容量失败:', error)
} }
return timeSlots
} }
//
const handleVenueChange = () => {
form.value.time_slot = ''
//
const selectedVenue = venueList.value.find(item => item.id === form.value.venue_id)
if (selectedVenue) {
timeSlotOptions.value = generateTimeSlots(selectedVenue)
//
maxSeatSelections.value = selectedVenue.capacity || 30
console.log('场地容量:', selectedVenue.capacity, '最大选择数:', maxSeatSelections.value)
} else {
timeSlotOptions.value = []
maxSeatSelections.value = 30
console.log('未找到场地,使用默认容量:', maxSeatSelections.value)
}
availableCapacity.value = 0
occupiedSeats.value = []
//
form.value.selectedSeats = []
selectedStudentSeats.value = []
}
//
const handleSeatSelectionChange = (selectedSeats) => {
console.log('选中的座位:', selectedSeats);
//
if (form.value.course_type === 'student' || form.value.course_type === 'trial') {
const students = studentList.value.filter(student => form.value.student_ids.includes(student.id));
//
selectedStudentSeats.value = selectedSeats.map((seatId, index) => {
const student = students[index];
return {
id: seatId,
name: student ? student.name : ''
};
});
}
};
//
const updateSelectedStudentSeats = (seatObjects) => {
selectedStudentSeats.value = seatObjects;
console.log('选中的学生座位:', selectedStudentSeats.value);
};
//
const handleClassStudentSelectionChange = (studentId, checked) => {
//
const studentIndex = classStudentList.value.findIndex(s => s.id === studentId);
if (studentIndex !== -1) {
classStudentList.value[studentIndex].checked = checked;
//
const filteredIndex = filteredClassStudentList.value.findIndex(s => s.id === studentId);
if (filteredIndex !== -1) {
filteredClassStudentList.value[filteredIndex].checked = checked;
}
// ID
if (checked) {
if (!form.value.student_ids.includes(studentId)) {
form.value.student_ids.push(studentId);
}
} else {
const idIndex = form.value.student_ids.indexOf(studentId);
if (idIndex !== -1) {
form.value.student_ids.splice(idIndex, 1);
}
}
console.log('选中的班级学员IDs:', form.value.student_ids);
}
};
// //
const handleCourseTypeChange = () => { const handleCourseTypeChange = async () => {
// //
form.value.class_ids = [] form.value.class_ids = []
form.value.student_ids = [] form.value.student_ids = []
form.value.course_name = '' form.value.course_name = ''
selectedStudentSeats.value = [] //
classStudentList.value = [] //
filteredClassStudentList.value = [] //
// //
if (form.value.course_type === 'student' || form.value.course_type === 'trial') { if (form.value.course_type === 'student' || form.value.course_type === 'trial') {
loadStudentList() loadStudentList()
} } else if (form.value.course_type === 'class' && form.value.campus_id) {
} //
try {
// const response = await getClassroompeople(form.value.campus_id)
const handleClassChange = () => { if (response.data && Array.isArray(response.data)) {
// // API
if (form.value.class_ids.length > 0) { classStudentList.value = response.data.map(student => ({
const selectedClass = classList.value.find(item => item.id === form.value.class_ids[0]) id: student.id,
if (selectedClass) { name: student.name || '未命名学员',
form.value.course_name = selectedClass.class_name checked: false //
}))
filteredClassStudentList.value = [...classStudentList.value]
console.log('获取到的班级学员:', classStudentList.value)
} else {
//
classStudentList.value = Array.from({ length: 20 }, (_, i) => ({
id: i + 1,
name: `班级学员${i + 1}`,
checked: false
}))
filteredClassStudentList.value = [...classStudentList.value]
}
} catch (error) {
console.error('获取班级学员失败:', error)
//
classStudentList.value = Array.from({ length: 20 }, (_, i) => ({
id: i + 1,
name: `班级学员${i + 1}`,
checked: false
}))
filteredClassStudentList.value = [...classStudentList.value]
} }
} }
} }
@ -355,11 +540,54 @@ const searchStudents = () => {
) )
} }
//
const searchClassStudents = () => {
if (!classStudentSearchKeyword.value) {
filteredClassStudentList.value = [...classStudentList.value]
return
}
filteredClassStudentList.value = classStudentList.value.filter(student =>
student.name.includes(classStudentSearchKeyword.value)
)
}
// //
const cancel = () => { const cancel = () => {
dialogVisible.value = false dialogVisible.value = false
} }
//
const handleTimeSlotChange = async () => {
if (!form.value.venue_id || !form.value.time_slot || !form.value.course_date) {
participants.value = []
return
}
//
try {
const params = {
course_date: form.value.course_date,
time_slot: form.value.time_slot
}
const response = await getClassroompeopleCount(form.value.venue_id, params)
if (response.data) {
participants.value = response.data
console.log('获取到的人员信息:', participants.value)
}
} catch (error) {
console.error('获取人员信息失败:', error)
participants.value = []
}
}
// course_date
watch(() => form.value.course_date, (newVal) => {
if (newVal && form.value.venue_id && form.value.time_slot) {
handleTimeSlotChange()
}
})
// //
const submit = () => { const submit = () => {
formRef.value.validate(async (valid) => { formRef.value.validate(async (valid) => {
@ -376,17 +604,16 @@ const submit = () => {
participants: form.value.course_type, participants: form.value.course_type,
available_capacity: availableCapacity.value, available_capacity: availableCapacity.value,
status: 'active', status: 'active',
selected_seats: form.value.selectedSeats, //
participants_info: participants.value //
} }
// // 使student_ids
params.student_ids = form.value.student_ids
// class_ids
if (form.value.course_type === 'class') { if (form.value.course_type === 'class') {
params.class_ids = form.value.class_ids params.class_ids = form.value.class_ids
//
const classStudents = []
//
params.student_ids = classStudents
} else {
params.student_ids = form.value.student_ids
} }
const response = await addCourseSchedule(params) const response = await addCourseSchedule(params)
@ -414,8 +641,15 @@ const resetForm = () => {
class_ids: [], class_ids: [],
student_ids: [], student_ids: [],
course_name: '', course_name: '',
coach_id: '' coach_id: '',
selectedSeats: []
} }
selectedStudentSeats.value = [] //
maxSeatSelections.value = 30 //
classStudentList.value = [] //
filteredClassStudentList.value = [] //
classStudentSearchKeyword.value = '' //
studentSearchKeyword.value = '' //
if (formRef.value) { if (formRef.value) {
formRef.value.resetFields() formRef.value.resetFields()
} }
@ -431,4 +665,25 @@ const resetForm = () => {
padding: 10px; padding: 10px;
margin-top: 8px; margin-top: 8px;
} }
.class-student-list {
max-height: 200px;
overflow-y: auto;
border: 1px solid #EBEEF5;
border-radius: 4px;
padding: 10px;
margin-top: 8px;
}
.class-student-grid {
display: flex;
flex-wrap: wrap;
}
.class-student-item {
width: 25%; /* 一行4个 */
margin-bottom: 8px;
padding-right: 10px;
box-sizing: border-box;
}
</style> </style>

486
admin/src/app/views/timetables/components/seat-selector.vue

@ -0,0 +1,486 @@
<template>
<div class="seat-selector">
<div class="seats-container">
<div v-if="rows.length === 0" class="no-seats-message">
没有可用座位请确认场地容量设置
</div>
<div v-else v-for="row in rows" :key="row" class="seat-row">
<div
v-for="col in cols"
:key="`${row}-${col}`"
class="seat-container"
>
<div
class="seat"
:class="{
'seat-available': isSeatAvailable(row, col),
'seat-occupied': !isSeatAvailable(row, col) && isSeatValid(row, col),
'seat-selected': isSelected(row, col),
'seat-hidden': !isSeatValid(row, col)
}"
@click="toggleSeat(row, col)"
>
{{ col }}
</div>
<div v-if="isSelected(row, col)" class="seat-name">
{{ getSeatObject(row, col)?.name || '' }}
</div>
</div>
</div>
</div>
<div class="seat-legend">
<div class="legend-item">
<div class="legend-box seat-available"></div>
<span>可选</span>
</div>
<div class="legend-item">
<div class="legend-box seat-occupied"></div>
<span>已占</span>
</div>
<div class="legend-item">
<div class="legend-box seat-selected"></div>
<span>已选</span>
</div>
</div>
<div class="seat-info">
<span>已选: {{ selectedSeats.length }}/{{ props.maxSelections === Infinity ? '不限' : props.maxSelections }}</span>
<span>剩余空位: {{ availableSeats.length }}</span>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue';
import { getClassroompeople } from '@/app/api/classroom';
import { getAllVenueList } from '@/app/api/venue';
const props = defineProps({
venueId: {
type: [Number, String],
default: null
},
capacity: {
type: Number,
default: 0
},
occupiedSeats: {
type: Array,
default: () => []
},
selectedSeatIds: {
type: Array,
default: () => []
},
maxSelections: {
type: Number,
default: Infinity
}
});
const emit = defineEmits(['update:selectedSeats', 'change', 'update:selectedSeatIds']);
//
const rows = ref([]);
const cols = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); // 10
const selectedSeats = ref([]);
const occupiedSeatsData = ref([]);
const selectedSeatObjects = ref([]);
const totalSeats = ref(0);
// - 使onMountedwatch
const adjustLayout = (capacityValue) => {
console.log('已废弃的adjustLayout调用,使用值:', capacityValue);
};
//
const loadVenueInfoAndOccupiedSeats = async () => {
if (!props.venueId) return;
try {
// 使
if (props.occupiedSeats && props.occupiedSeats.length) {
occupiedSeatsData.value = props.occupiedSeats;
} else {
// API
const response = await getClassroompeople(props.venueId);
if (response.data && Array.isArray(response.data)) {
// API
occupiedSeatsData.value = response.data;
}
}
} catch (error) {
console.error('获取已占用位置失败:', error);
//
occupiedSeatsData.value = generateRandomOccupiedSeats();
}
};
//
const generateRandomOccupiedSeats = () => {
const occupied = [];
const totalSeats = rows.value.length * cols.value.length;
const occupiedCount = Math.floor(totalSeats * 0.3); // 30%
for (let i = 0; i < occupiedCount; i++) {
const row = rows.value[Math.floor(Math.random() * rows.value.length)];
const col = cols.value[Math.floor(Math.random() * cols.value.length)];
const seat = `${row}-${col}`;
if (!occupied.includes(seat)) {
occupied.push(seat);
}
}
return occupied;
};
//
const allValidSeats = computed(() => {
const seats = [];
if (rows.value.length === 0) return seats;
rows.value.forEach(row => {
cols.value.forEach(col => {
if (isSeatValid(row, col)) {
const seatId = `${row}-${col}`;
seats.push(seatId);
}
});
});
return seats;
});
//
const availableSeats = computed(() => {
const seats = [];
allValidSeats.value.forEach(seatId => {
if (!occupiedSeatsData.value.includes(seatId)) {
seats.push(seatId);
}
});
console.log('有效座位总数:', allValidSeats.value.length);
console.log('已占用座位数:', occupiedSeatsData.value.length);
console.log('可用座位数:', seats.length);
console.log('已选座位数:', selectedSeats.value.length);
return seats;
});
//
const isSeatValid = (row, col) => {
const seatIndex = (row.charCodeAt(0) - 65) * 10 + (col - 1);
return seatIndex < totalSeats.value;
};
//
const isSeatAvailable = (row, col) => {
//
if (!isSeatValid(row, col)) {
return false;
}
const seatId = `${row}-${col}`;
return !occupiedSeatsData.value.includes(seatId);
};
//
const isSelected = (row, col) => {
const seatId = `${row}-${col}`;
return selectedSeats.value.includes(seatId);
};
//
const getSeatObject = (row, col) => {
const seatId = `${row}-${col}`;
return selectedSeatObjects.value.find(obj => obj.id === seatId);
};
//
const toggleSeat = (row, col) => {
console.log('点击座位:', row, col);
//
if (!isSeatValid(row, col)) {
console.log('座位无效,不能选择');
return;
}
const seatId = `${row}-${col}`;
//
if (!isSeatAvailable(row, col)) {
console.log('座位已占用,不能选择');
return;
}
const index = selectedSeats.value.indexOf(seatId);
if (index === -1) {
//
if (selectedSeats.value.length >= props.maxSelections) {
console.log('已达到最大选择数量:', props.maxSelections);
return;
}
console.log('添加座位:', seatId);
selectedSeats.value.push(seatId);
//
if (!selectedSeatObjects.value.some(obj => obj.id === seatId)) {
selectedSeatObjects.value.push({ id: seatId, name: '' });
}
} else {
console.log('移除座位:', seatId);
selectedSeats.value.splice(index, 1);
//
const objIndex = selectedSeatObjects.value.findIndex(obj => obj.id === seatId);
if (objIndex !== -1) {
selectedSeatObjects.value.splice(objIndex, 1);
}
}
console.log('选中座位:', selectedSeats.value);
emit('update:selectedSeats', selectedSeats.value);
emit('update:selectedSeatIds', selectedSeatObjects.value);
emit('change', selectedSeats.value);
};
//
const venueList = ref([]);
//
const loadVenueList = async () => {
try {
const response = await getAllVenueList({});
if (response.data && Array.isArray(response.data)) {
venueList.value = response.data;
}
} catch (error) {
console.error('获取场地列表失败:', error);
}
};
//
const getCurrentVenueInfo = () => {
const venue = venueList.value.find(venue => venue.id === props.venueId) || null;
console.log('获取场地信息:', venue, '场地ID:', props.venueId, '场地列表:', venueList.value);
return venue;
};
//
watch(() => props.venueId, async () => {
console.log('监听venueId变化:', props.venueId);
selectedSeats.value = [];
selectedSeatObjects.value = [];
//
if (props.capacity) {
console.log('使用props容量:', props.capacity);
totalSeats.value = props.capacity;
const rowCount = Math.ceil(props.capacity / 10);
rows.value = Array.from({ length: rowCount }, (_, i) =>
String.fromCharCode(65 + i)
).slice(0, 26);
} else {
//
totalSeats.value = 30;
const rowCount = 3;
rows.value = Array.from({ length: rowCount }, (_, i) =>
String.fromCharCode(65 + i)
);
}
console.log('更新布局 - 总座位数:', totalSeats.value, '行数:', rows.value.length);
//
loadVenueInfoAndOccupiedSeats();
});
//
watch(() => props.capacity, (newCapacity) => {
console.log('监听capacity变化:', newCapacity);
//
if (newCapacity || newCapacity === 0) {
totalSeats.value = newCapacity;
const rowCount = Math.ceil(newCapacity / 10);
rows.value = Array.from({ length: rowCount }, (_, i) =>
String.fromCharCode(65 + i)
).slice(0, 26);
console.log('根据新容量更新布局 - 总座位数:', totalSeats.value, '行数:', rows.value.length);
}
});
watch(() => props.occupiedSeats, () => {
occupiedSeatsData.value = props.occupiedSeats;
});
// selectedSeatIds
watch(() => props.selectedSeatIds, (newValue) => {
if (newValue && Array.isArray(newValue)) {
selectedSeatObjects.value = [...newValue];
selectedSeats.value = newValue.map(item => item.id);
}
}, { immediate: true });
//
onMounted(async () => {
console.log('组件挂载 - venueId:', props.venueId, 'capacity:', props.capacity);
//
if (!props.capacity && props.capacity !== 0) {
totalSeats.value = 30;
//
const rowCount = Math.ceil(totalSeats.value / 10);
rows.value = Array.from({ length: rowCount }, (_, i) =>
String.fromCharCode(65 + i)
);
console.log('使用默认设置 - 总座位数:', totalSeats.value, '行数:', rows.value.length);
} else {
console.log('使用props容量:', props.capacity);
totalSeats.value = props.capacity;
//
const rowCount = Math.ceil(props.capacity / 10);
//
rows.value = Array.from({ length: rowCount }, (_, i) =>
String.fromCharCode(65 + i)
).slice(0, 26);
console.log('根据容量设置 - 总座位数:', totalSeats.value, '行数:', rows.value.length);
}
//
loadVenueInfoAndOccupiedSeats();
});
</script>
<style scoped>
.seat-selector {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.screen {
width: 80%;
height: 30px;
background: linear-gradient(to bottom, #ccc, #f5f5f5);
margin-bottom: 30px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 5px;
}
.screen-text {
font-size: 14px;
color: #666;
}
.seats-container {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 20px;
}
.seat-row {
display: flex;
align-items: flex-start;
gap: 10px;
}
.row-label {
width: 20px;
text-align: center;
font-weight: bold;
margin-top: 10px;
}
.seat-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
margin-bottom: 5px;
}
.seat {
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 5px;
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.seat-name {
font-size: 10px;
max-width: 40px;
text-align: center;
word-break: break-all;
color: #333;
height: 24px;
overflow: hidden;
}
.seat-available {
background-color: #67c23a;
color: white;
}
.seat-occupied {
background-color: #909399;
color: white;
cursor: not-allowed;
}
.seat-selected {
background-color: #409eff;
color: white;
}
.seat-hidden {
visibility: hidden;
pointer-events: none;
}
.seat-legend {
display: flex;
gap: 20px;
margin-bottom: 15px;
}
.legend-item {
display: flex;
align-items: center;
gap: 5px;
}
.legend-box {
width: 15px;
height: 15px;
border-radius: 3px;
}
.seat-info {
display: flex;
gap: 20px;
font-size: 14px;
}
.no-seats-message {
font-size: 14px;
color: #666;
padding: 20px;
text-align: center;
}
</style>

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

@ -1,7 +1,7 @@
<template> <template>
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<div class="mb-4 flex items-center justify-between"> <div class="mb-4 flex items-center justify-between">
<div class="flex items-center"> <div class="flex items-center" style="width: 50%;">
<el-select <el-select
v-model="selectedCampus" v-model="selectedCampus"
placeholder="请选择校区" placeholder="请选择校区"
@ -17,14 +17,16 @@
/> />
</el-select> </el-select>
<el-button @click="prevWeek" icon="el-icon-arrow-left">上一周</el-button> <el-button @click="prevWeek" icon="el-icon-arrow-left">上一周</el-button>
<el-date-picker <div class="ml-2 mr-2">
<el-date-picker
v-model="weekDate" v-model="weekDate"
type="week" type="week"
format="YYYY 第 ww 周" format="YYYY 第 ww 周"
placeholder="选择周" placeholder="选择周"
class="week-picker" style="width: 180px;"
@change="handleWeekChange" @change="handleWeekChange"
/> />
</div>
<el-button @click="nextWeek" icon="el-icon-arrow-right">下一周</el-button> <el-button @click="nextWeek" icon="el-icon-arrow-right">下一周</el-button>
<el-button type="primary" class="ml-2" @click="fetchData">查询</el-button> <el-button type="primary" class="ml-2" @click="fetchData">查询</el-button>
</div> </div>
@ -49,23 +51,18 @@
width="80" width="80"
align="center" align="center"
> >
<template #default="{ row }">
<div :style="{ backgroundColor: row.color }">
{{ row.timeRange }}
</div>
</template>
</el-table-column> </el-table-column>
<!-- 教室列 --> <!-- 教室列 -->
<el-table-column <el-table-column
v-for="(classroom, idx) in day.classrooms" v-for="(classroom, idx) in day.classrooms"
:key="idx" :key="idx"
:label="`教室${classroom}`" :label="`${classroom.venue_name}`"
:prop="`classroom${classroom}`" :prop="`classroom${classroom.id}`"
align="center" align="center"
> >
<template #default="{ row }"> <template #default="{ row }">
<div v-if="row.course && row.course.classroom == classroom"> <div v-if="row.course && row.course.classroom.id == classroom.id">
<div class="teacher-name"> <div class="teacher-name">
{{ getTeacherName(row.course.teacher) }} {{ getTeacherName(row.course.teacher) }}
</div> </div>
@ -314,8 +311,4 @@ onMounted(() => {
.classroom-name { .classroom-name {
margin-bottom: 5px; margin-bottom: 5px;
} }
.week-picker {
width: 180px;
}
</style> </style>

113
admin/src/utils/timeslots.ts

@ -0,0 +1,113 @@
/**
*
* @param venueInfo
* @returns ["08:00-09:00", "09:00-10:00"]
*/
export const generateTimeSlots = (venueInfo: any) => {
if (!venueInfo) return []
const timeSlots: string[] = []
// 如果场地已有预设时间段,优先使用
if (venueInfo.time_slots && Array.isArray(venueInfo.time_slots)) {
return venueInfo.time_slots.map((slot: string) => slot.trim());
}
// 根据time_range_type生成不同的时间段
switch (venueInfo.time_range_type) {
case 'fixed':
// 从fixed_time_ranges中获取固定时间段
try {
// 确保fixed_time_ranges是数组
const fixedRanges = Array.isArray(venueInfo.fixed_time_ranges)
? venueInfo.fixed_time_ranges
: (typeof venueInfo.fixed_time_ranges === 'string'
? JSON.parse(venueInfo.fixed_time_ranges || '[]')
: []);
fixedRanges.forEach((range: any) => {
// 确保start_time和end_time存在
if (range && range.start_time && range.end_time) {
timeSlots.push(`${range.start_time}-${range.end_time}`)
}
})
} catch (error) {
console.error('解析固定时间段失败:', error)
}
break
case 'all':
// 全天可用,但中午12:30-14:00不可用
// 早上8点到中午12:30,步长60分钟
for (let hour = 8; hour <= 12; hour++) {
for (let minute = 0; minute < 60; minute += 60) {
if (hour === 12 && minute === 30) continue // 跳过12:30
const startHour = hour
const startMinute = minute
const endHour = minute === 30 ? hour + 1 : hour
const endMinute = minute === 30 ? 0 : 30
const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}`
const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}`
timeSlots.push(`${startTime}-${endTime}`)
}
}
// 下午14:00到晚上22:00,步长60分钟
for (let hour = 14; hour < 22; hour++) {
for (let minute = 0; minute < 60; minute += 60) {
const startHour = hour
const startMinute = minute
const endHour = minute === 30 ? hour + 1 : hour
const endMinute = minute === 30 ? 0 : 30
const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}`
const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}`
timeSlots.push(`${startTime}-${endTime}`)
}
}
break
case 'range':
// 使用指定的时间范围,步长60分钟
if (venueInfo.time_range_start && venueInfo.time_range_end) {
const startTimeParts = venueInfo.time_range_start.split(':')
const endTimeParts = venueInfo.time_range_end.split(':')
if (startTimeParts.length === 2 && endTimeParts.length === 2) {
const startHour = parseInt(startTimeParts[0])
const startMinute = parseInt(startTimeParts[1])
const endHour = parseInt(endTimeParts[0])
const endMinute = parseInt(endTimeParts[1])
// 计算总分钟数
const startTotalMinutes = startHour * 60 + startMinute
const endTotalMinutes = endHour * 60 + endMinute
// 以60分钟为步长生成时间段
for (let minutes = startTotalMinutes; minutes < endTotalMinutes; minutes += 60) {
const startHour = Math.floor(minutes / 60)
const startMinute = minutes % 60
const endHour = Math.floor((minutes + 60) / 60)
const endMinute = (minutes + 60) % 60
const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}`
const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}`
timeSlots.push(`${startTime}-${endTime}`)
}
}
}
break
}
// 添加现有时间段,如果有的话
if (venueInfo.time_slot && !timeSlots.includes(venueInfo.time_slot)) {
timeSlots.push(venueInfo.time_slot);
}
return timeSlots
}

6642
admin/yarn.lock

File diff suppressed because it is too large

102
niucloud/app/adminapi/controller/classroom/Classroom.php

@ -22,19 +22,20 @@ use app\service\admin\classroom\ClassroomService;
*/ */
class Classroom extends BaseAdminController class Classroom extends BaseAdminController
{ {
/** /**
* 获取场地管理列表 * 获取场地管理列表
* @return \think\Response * @return \think\Response
*/ */
public function lists(){ public function lists()
{
$data = $this->request->params([ $data = $this->request->params([
["campus_id",""], ["campus_id", ""],
["class_name",""], ["class_name", ""],
["head_coach",""], ["head_coach", ""],
["class_type",""], ["class_type", ""],
["assistant_coach",""], ["assistant_coach", ""],
["created_at",""], ["created_at", ""],
["status",""] ["status", ""]
]); ]);
return success((new ClassroomService())->getPage($data)); return success((new ClassroomService())->getPage($data));
} }
@ -44,7 +45,8 @@ class Classroom extends BaseAdminController
* @param int $id * @param int $id
* @return \think\Response * @return \think\Response
*/ */
public function info(int $id){ public function info(int $id)
{
return success((new ClassroomService())->getInfo($id)); return success((new ClassroomService())->getInfo($id));
} }
@ -52,17 +54,18 @@ class Classroom extends BaseAdminController
* 添加场地管理 * 添加场地管理
* @return \think\Response * @return \think\Response
*/ */
public function add(){ public function add()
{
$data = $this->request->params([ $data = $this->request->params([
["campus_id",0], ["campus_id", 0],
["class_name",""], ["class_name", ""],
["head_coach",""], ["head_coach", ""],
["age_group",""], ["age_group", ""],
["class_type",""], ["class_type", ""],
["assistant_coach",""], ["assistant_coach", ""],
["status",""], ["status", ""],
["sort_order",0], ["sort_order", 0],
["remarks",""] ["remarks", ""]
]); ]);
$this->validate($data, 'app\validate\classroom\Classroom.add'); $this->validate($data, 'app\validate\classroom\Classroom.add');
$id = (new ClassroomService())->add($data); $id = (new ClassroomService())->add($data);
@ -74,17 +77,18 @@ class Classroom extends BaseAdminController
* @param $id 场地管理id * @param $id 场地管理id
* @return \think\Response * @return \think\Response
*/ */
public function edit(int $id){ public function edit(int $id)
{
$data = $this->request->params([ $data = $this->request->params([
["campus_id",0], ["campus_id", 0],
["class_name",""], ["class_name", ""],
["head_coach",""], ["head_coach", ""],
["age_group",""], ["age_group", ""],
["class_type",""], ["class_type", ""],
["assistant_coach",""], ["assistant_coach", ""],
["status",""], ["status", ""],
["sort_order",0], ["sort_order", 0],
["remarks",""] ["remarks", ""]
]); ]);
$this->validate($data, 'app\validate\classroom\Classroom.edit'); $this->validate($data, 'app\validate\classroom\Classroom.edit');
(new ClassroomService())->edit($id, $data); (new ClassroomService())->edit($id, $data);
@ -96,22 +100,40 @@ class Classroom extends BaseAdminController
* @param $id 场地管理id * @param $id 场地管理id
* @return \think\Response * @return \think\Response
*/ */
public function del(int $id){ public function del(int $id)
{
(new ClassroomService())->del($id); (new ClassroomService())->del($id);
return success('DELETE_SUCCESS'); return success('DELETE_SUCCESS');
} }
public function getCampusAll(){ public function getCampusAll()
return success(( new ClassroomService())->getCampusAll()); {
return success((new ClassroomService())->getCampusAll());
} }
public function getPersonnelAll(){ public function getPersonnelAll()
return success(( new ClassroomService())->getPersonnelAll()); {
return success((new ClassroomService())->getPersonnelAll());
} }
public function classroom_all() public function classroom_all()
{ {
return success(( new ClassroomService())->classroom_all()); return success((new ClassroomService())->classroom_all());
} }
public function getClassroompeople($class_id)
{
return success((new ClassroomService())->getClassroompeople($class_id));
}
public function getClassroompeopleCount($venue_id)
{
$where = $this->request->params([
["course_date", ""],
["time_slot", ""]
]);
return success((new ClassroomService())->getClassroompeopleCount($venue_id,$where));
}
} }

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

@ -22,21 +22,22 @@ use app\service\admin\course\CourseService;
*/ */
class Course extends BaseAdminController class Course extends BaseAdminController
{ {
/** /**
* 获取课程列表 * 获取课程列表
* @return \think\Response * @return \think\Response
*/ */
public function lists(){ public function lists()
{
$data = $this->request->params([ $data = $this->request->params([
["course_name",""], ["course_name", ""],
["course_type",""], ["course_type", ""],
["duration",""], ["duration", ""],
["session_count",""], ["session_count", ""],
["single_session_count",""], ["single_session_count", ""],
["price",""], ["price", ""],
["internal_reminder",""], ["internal_reminder", ""],
["customer_reminder",""], ["customer_reminder", ""],
["remarks",""] ["remarks", ""]
]); ]);
return success((new CourseService())->getPage($data)); return success((new CourseService())->getPage($data));
} }
@ -46,7 +47,8 @@ class Course extends BaseAdminController
* @param int $id * @param int $id
* @return \think\Response * @return \think\Response
*/ */
public function info(int $id){ public function info(int $id)
{
return success((new CourseService())->getInfo($id)); return success((new CourseService())->getInfo($id));
} }
@ -54,17 +56,18 @@ class Course extends BaseAdminController
* 添加课程 * 添加课程
* @return \think\Response * @return \think\Response
*/ */
public function add(){ public function add()
{
$data = $this->request->params([ $data = $this->request->params([
["course_name",""], ["course_name", ""],
["course_type",""], ["course_type", ""],
["duration",0], ["duration", 0],
["session_count",0], ["session_count", 0],
["single_session_count",0], ["single_session_count", 0],
["price",0.00], ["price", 0.00],
["internal_reminder",0], ["internal_reminder", 0],
["customer_reminder",0], ["customer_reminder", 0],
["remarks",""], ["remarks", ""],
]); ]);
$this->validate($data, 'app\validate\course\Course.add'); $this->validate($data, 'app\validate\course\Course.add');
@ -77,20 +80,21 @@ class Course extends BaseAdminController
* @param $id 课程id * @param $id 课程id
* @return \think\Response * @return \think\Response
*/ */
public function edit(int $id){ public function edit(int $id)
{
$data = $this->request->params([ $data = $this->request->params([
["course_name",""], ["course_name", ""],
["course_type",""], ["course_type", ""],
["duration",0], ["duration", 0],
["session_count",0], ["session_count", 0],
["single_session_count",0], ["single_session_count", 0],
["price",0.00], ["price", 0.00],
["internal_reminder",0], ["internal_reminder", 0],
["customer_reminder",0], ["customer_reminder", 0],
["remarks",""], ["remarks", ""],
["contract_id",""], ["contract_id",""],
]); ]);
// $this->validate($data, 'app\validate\course\Course.edit'); $this->validate($data, 'app\validate\course\Course.edit');
(new CourseService())->edit($id, $data); (new CourseService())->edit($id, $data);
return success('EDIT_SUCCESS'); return success('EDIT_SUCCESS');
} }
@ -100,11 +104,28 @@ class Course extends BaseAdminController
* @param $id 课程id * @param $id 课程id
* @return \think\Response * @return \think\Response
*/ */
public function del(int $id){ public function del(int $id)
{
(new CourseService())->del($id); (new CourseService())->del($id);
return success('DELETE_SUCCESS'); return success('DELETE_SUCCESS');
} }
public function getAllCourseList()
{
$data = $this->request->params([
["course_name", ""],
["course_type", ""],
["duration", ""],
["session_count", ""],
["single_session_count", ""],
["price", ""],
["internal_reminder", ""],
["customer_reminder", ""],
["remarks", ""]
]);
return success((new CourseService())->getAllCourseList($data));
}
public function contract_all(){ public function contract_all(){
return success((new CourseService())->contract_all()); return success((new CourseService())->contract_all());
} }

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

@ -22,22 +22,23 @@ use app\service\admin\course_schedule\CourseScheduleService;
*/ */
class CourseSchedule extends BaseAdminController class CourseSchedule extends BaseAdminController
{ {
/** /**
* 获取课程安排列表 * 获取课程安排列表
* @return \think\Response * @return \think\Response
*/ */
public function lists(){ public function lists()
{
$data = $this->request->params([ $data = $this->request->params([
["campus_id",""], ["campus_id", ""],
["venue_id",""], ["venue_id", ""],
["course_date",""], ["course_date", ""],
["time_slot",""], ["time_slot", ""],
["course_id",""], ["course_id", ""],
["coach_id",""], ["coach_id", ""],
["participants",""], ["participants", ""],
["student_ids",""], ["student_ids", ""],
["available_capacity",""], ["available_capacity", ""],
["status",""] ["status", ""]
]); ]);
return success((new CourseScheduleService())->getPage($data)); return success((new CourseScheduleService())->getPage($data));
} }
@ -47,7 +48,8 @@ class CourseSchedule extends BaseAdminController
* @param mixed $id * @param mixed $id
* @return \think\Response * @return \think\Response
*/ */
public function info($id){ public function info($id)
{
// 确保 $id 是整数类型 // 确保 $id 是整数类型
$id = intval($id); $id = intval($id);
return success((new CourseScheduleService())->getInfo($id)); return success((new CourseScheduleService())->getInfo($id));
@ -57,19 +59,20 @@ class CourseSchedule extends BaseAdminController
* 添加课程安排 * 添加课程安排
* @return \think\Response * @return \think\Response
*/ */
public function add(){ public function add()
{
$data = $this->request->params([ $data = $this->request->params([
["campus_id",0], ["campus_id", 0],
["venue_id",0], ["venue_id", 0],
["course_date","2025-05-16 17:28:06"], ["course_date", "2025-05-16 17:28:06"],
["time_slot",""], ["time_slot", ""],
["course_id",0], ["course_id", 0],
["coach_id",0], ["coach_id", 0],
["participants",""], ["participants", ""],
["student_ids",""], ["student_ids", ""],
["available_capacity",0], ["available_capacity", 0],
["status",""], ["status", ""],
['is_system_add', 1]
]); ]);
$this->validate($data, 'app\validate\course_schedule\CourseSchedule.add'); $this->validate($data, 'app\validate\course_schedule\CourseSchedule.add');
$id = (new CourseScheduleService())->add($data); $id = (new CourseScheduleService())->add($data);
@ -81,19 +84,20 @@ class CourseSchedule extends BaseAdminController
* @param $id 课程安排id * @param $id 课程安排id
* @return \think\Response * @return \think\Response
*/ */
public function edit(int $id){ public function edit(int $id)
{
$data = $this->request->params([ $data = $this->request->params([
["campus_id",0], ["campus_id", 0],
["venue_id",0], ["venue_id", 0],
["course_date","2025-05-16 17:28:06"], ["course_date", "2025-05-16 17:28:06"],
["time_slot",""], ["time_slot", ""],
["course_id",0], ["course_id", 0],
["coach_id",0], ["coach_id", 0],
["participants",""], ["participants", ""],
["student_ids",""], ["student_ids", ""],
["available_capacity",0], ["available_capacity", 0],
["status",""], ["status", ""],
['is_system_add', 1]
]); ]);
$this->validate($data, 'app\validate\course_schedule\CourseSchedule.edit'); $this->validate($data, 'app\validate\course_schedule\CourseSchedule.edit');
(new CourseScheduleService())->edit($id, $data); (new CourseScheduleService())->edit($id, $data);
@ -105,24 +109,27 @@ class CourseSchedule extends BaseAdminController
* @param $id 课程安排id * @param $id 课程安排id
* @return \think\Response * @return \think\Response
*/ */
public function del(int $id){ public function del(int $id)
{
(new CourseScheduleService())->del($id); (new CourseScheduleService())->del($id);
return success('DELETE_SUCCESS'); return success('DELETE_SUCCESS');
} }
/** /**
* 获取课程表数据 * 获取课程表数据
* @return \think\Response * @return \think\Response
*/ */
public function timetables(){ public function timetables()
{
$data = $this->request->params([ $data = $this->request->params([
["start_date", ""], ["start_date", ""],
["end_date", ""], ["end_date", ""],
["campus_id", ""], ["campus_id", ""],
["venue_id", ""] ["venue_id", ""]
]); ]);
return success((new CourseScheduleService())->getTimetables($data)); return success((new CourseScheduleService())->getTimetables($data));
} }
public function getCampusVenue() public function getCampusVenue()
{ {
$data = $this->request->params([ $data = $this->request->params([

195
niucloud/app/adminapi/controller/customer_resources/CustomerResources.php

@ -22,20 +22,21 @@ use app\service\admin\customer_resources\CustomerResourcesService;
*/ */
class CustomerResources extends BaseAdminController class CustomerResources extends BaseAdminController
{ {
/** /**
* 获取客户资源列表 * 获取客户资源列表
* @return \think\Response * @return \think\Response
*/ */
public function lists(){ public function lists()
{
$data = $this->request->params([ $data = $this->request->params([
["name",""], ["name", ""],
["age",""], ["age", ""],
["gender",""], ["gender", ""],
["phone_number",""], ["phone_number", ""],
['member_label', 0], ['member_label', 0],
["type","khzy"], ["type", "khzy"],
["created_at",[]], ["created_at", []],
["updated_at",[]], ["updated_at", []],
]); ]);
return success((new CustomerResourcesService())->getPage($data)); return success((new CustomerResourcesService())->getPage($data));
} }
@ -45,7 +46,8 @@ class CustomerResources extends BaseAdminController
* @param int $id * @param int $id
* @return \think\Response * @return \think\Response
*/ */
public function info(int $id){ public function info(int $id)
{
return success((new CustomerResourcesService())->getInfo($id)); return success((new CustomerResourcesService())->getInfo($id));
} }
@ -53,38 +55,39 @@ class CustomerResources extends BaseAdminController
* 添加客户资源 * 添加客户资源
* @return \think\Response * @return \think\Response
*/ */
public function add(){ public function add()
{
$data = $this->request->params([ $data = $this->request->params([
["source",""], ["source", ""],
["source_channel",""], ["source_channel", ""],
["name",""], ["name", ""],
["age",0], ["age", 0],
["gender",""], ["gender", ""],
["phone_number",""], ["phone_number", ""],
["demand",""], ["demand", ""],
["purchasing_power",""], ["purchasing_power", ""],
["cognitive_idea",""], ["cognitive_idea", ""],
["optional_class_time",""], ["optional_class_time", ""],
["distance",""], ["distance", ""],
["decision_maker",""], ["decision_maker", ""],
["initial_intent",null], ["initial_intent", null],
["campus",""], ["campus", ""],
["status",null], ["status", null],
['member_label', []], ['member_label', []],
["create_year_month",date("Y-m")], ["create_year_month", date("Y-m")],
["create_date",date("Y-m-d")], ["create_date", date("Y-m-d")],
["purchase_power",""], ["purchase_power", ""],
["concept_awareness",""], ["concept_awareness", ""],
["preferred_class_time",""], ["preferred_class_time", ""],
["distance_tow",""], ["distance_tow", ""],
["communication",""], ["communication", ""],
["promised_visit_time",""], ["promised_visit_time", ""],
["actual_visit_time",""], ["actual_visit_time", ""],
["call_intent","low"], ["call_intent", "low"],
["first_visit_status",""], ["first_visit_status", ""],
["second_visit_status",""], ["second_visit_status", ""],
["is_closed",""] ["is_closed", ""]
]); ]);
$this->validate($data, 'app\validate\customer_resources\CustomerResources.add'); $this->validate($data, 'app\validate\customer_resources\CustomerResources.add');
@ -96,38 +99,39 @@ class CustomerResources extends BaseAdminController
* @param $id 客户资源id * @param $id 客户资源id
* @return \think\Response * @return \think\Response
*/ */
public function edit(int $id){ public function edit(int $id)
{
$data = $this->request->params([ $data = $this->request->params([
["source",""], ["source", ""],
["source_channel",""], ["source_channel", ""],
["name",""], ["name", ""],
["age",0], ["age", 0],
["gender",""], ["gender", ""],
["phone_number",""], ["phone_number", ""],
["demand",""], ["demand", ""],
["purchasing_power",""], ["purchasing_power", ""],
["cognitive_idea",""], ["cognitive_idea", ""],
["optional_class_time",""], ["optional_class_time", ""],
["distance",""], ["distance", ""],
["decision_maker",""], ["decision_maker", ""],
["initial_intent",null], ["initial_intent", null],
["campus",""], ["campus", ""],
["status",null], ["status", null],
['member_label', []], ['member_label', []],
["create_year_month",date("Y-m")], ["create_year_month", date("Y-m")],
["create_date",date("Y-m-d")], ["create_date", date("Y-m-d")],
["purchase_power",""], ["purchase_power", ""],
["concept_awareness",""], ["concept_awareness", ""],
["preferred_class_time",""], ["preferred_class_time", ""],
["distance_tow",""], ["distance_tow", ""],
["communication",""], ["communication", ""],
["promised_visit_time",""], ["promised_visit_time", ""],
["actual_visit_time",""], ["actual_visit_time", ""],
["call_intent","low"], ["call_intent", "low"],
["first_visit_status",""], ["first_visit_status", ""],
["second_visit_status",""], ["second_visit_status", ""],
["is_closed",""] ["is_closed", ""]
]); ]);
$this->validate($data, 'app\validate\customer_resources\CustomerResources.edit'); $this->validate($data, 'app\validate\customer_resources\CustomerResources.edit');
@ -139,32 +143,51 @@ class CustomerResources extends BaseAdminController
* @param $id 客户资源id * @param $id 客户资源id
* @return \think\Response * @return \think\Response
*/ */
public function del(int $id){ public function del(int $id)
{
(new CustomerResourcesService())->del($id); (new CustomerResourcesService())->del($id);
return success('DELETE_SUCCESS'); return success('DELETE_SUCCESS');
} }
public function getPersonnelAll(){ public function getPersonnelAll()
{
$data = $this->request->params([ $data = $this->request->params([
["role_id",""], ["role_id", ""],
]); ]);
return success(( new CustomerResourcesService())->getPersonnelAll($data)); return success((new CustomerResourcesService())->getPersonnelAll($data));
} }
public function getCampusAll(){ public function getCampusAll()
return success(( new CustomerResourcesService())->getCampusAll()); {
return success((new CustomerResourcesService())->getCampusAll());
} }
public function fp_edit(){ public function fp_edit()
{
$data = $this->request->params([ $data = $this->request->params([
["shared_id",""], ["shared_id", ""],
["shared_by",""], ["shared_by", ""],
]); ]);
return success(( new CustomerResourcesService())->fp_edit($data)); return success((new CustomerResourcesService())->fp_edit($data));
} }
public function personnelAllByname()
{
$data = $this->request->params([
["name", ""]
]);
return success((new CustomerResourcesService())->personnelAllByname($data['name']));
}
public function getCoachPerson()
{
$data = $this->request->params([
["campus_id", ""]
]);
return success((new CustomerResourcesService())->getCoachPerson($data['campus_id']));
}
} }

19
niucloud/app/adminapi/controller/lesson_course_teaching/LessonCourseTeaching.php

@ -759,4 +759,23 @@ class LessonCourseTeaching extends BaseAdminController
return success((new LessonCourseTeachingService())->getPersonnelDataAll($data)); return success((new LessonCourseTeachingService())->getPersonnelDataAll($data));
} }
public function getTestPaperList(){
$data = $this->request->params([
["total_score",""],
["selection_mode",""]
]);
return success((new LessonCourseTeachingService())->getTestPaperList($data));
}
public function bindingTestModule(int $id){
$data = $this->request->params([
["exam_papers_id",""],
["id",0],
]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->bindingTestModule($id, $data);
return success('ADD_SUCCESS', ['id' => $id]);
}
} }

11
niucloud/app/adminapi/controller/student/Student.php

@ -126,4 +126,15 @@ class Student extends BaseAdminController
return success(( new StudentService() )->label_all()); return success(( new StudentService() )->label_all());
} }
/**
* 通过名称查询学员
*/
public function getStudentByName()
{
$data = $this->request->params([
["name", ""],
]);
$list = (new StudentService())->getStudentByName($data['name']);
return success($list);
}
} }

2
niucloud/app/adminapi/controller/venue/Venue.php

@ -81,6 +81,8 @@ class Venue extends BaseAdminController
["campus_id", 0], ["campus_id", 0],
["venue_name", ""], ["venue_name", ""],
["capacity", 0], ["capacity", 0],
["time_range_start", 0],
["time_range_end", 0],
["availability_status", 0], ["availability_status", 0],
["time_range_type", ""], ["time_range_type", ""],
["fixed_time_ranges", []], ["fixed_time_ranges", []],

65
niucloud/app/adminapi/route/class.php

@ -1,65 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- campus
Route::group('campus', function () {
//校区列表
Route::get('campus', 'campus.Campus/lists');
//校区详情
Route::get('campus/:id', 'campus.Campus/info');
//添加校区
Route::post('campus', 'campus.Campus/add');
//编辑校区
Route::put('campus/:id', 'campus.Campus/edit');
//删除校区
Route::delete('campus/:id', 'campus.Campus/del');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- campus
// USER_CODE_BEGIN -- class
Route::group('class', function () {
//场地管理列表
Route::get('class', 'class.Class/lists');
//场地管理详情
Route::get('class/:id', 'class.Class/info');
//添加场地管理
Route::post('class', 'class.Class/add');
//编辑场地管理
Route::put('class/:id', 'class.Class/edit');
//删除场地管理
Route::delete('class/:id', 'class.Class/del');
Route::get('campus_all','class.Class/getCampusAll');
Route::get('personnel_all','class.Class/getPersonnelAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- class

4
niucloud/app/adminapi/route/classroom.php

@ -35,6 +35,10 @@ Route::group('classroom', function () {
Route::get('classroom_all','classroom.Classroom/classroom_all'); Route::get('classroom_all','classroom.Classroom/classroom_all');
Route::get('getClassroompeople/:class_id','classroom.Classroom/getClassroompeople');
Route::get('getClassroompeopleCount/:venue_id','classroom.Classroom/getClassroompeopleCount');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

3
niucloud/app/adminapi/route/course.php

@ -31,6 +31,9 @@ Route::group('course', function () {
Route::get('contract_all', 'course.Course/contract_all'); Route::get('contract_all', 'course.Course/contract_all');
//获取课程列表
Route::get('getAllCourseList', 'course.Course/getAllCourseList');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

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

@ -31,6 +31,8 @@ Route::group('course_schedule', function () {
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'); Route::get('campus_venue', 'course_schedule.CourseSchedule/getCampusVenue');
Route::get('timetables', 'course_schedule.CourseSchedule/timetables');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,

3
niucloud/app/adminapi/route/customer_resources.php

@ -41,6 +41,9 @@ Route::group('customer_resources', function () {
Route::post('fp_edit', 'customer_resources.CustomerResources/fp_edit'); Route::post('fp_edit', 'customer_resources.CustomerResources/fp_edit');
Route::post('personnel_all_byname', 'customer_resources.CustomerResources/personnelAllByname');
Route::get('coach_person', 'customer_resources.CustomerResources/getCoachPerson');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,

3
niucloud/app/adminapi/route/lesson_course_teaching.php

@ -116,6 +116,9 @@ Route::group('lesson_course_teaching', function () {
Route::get('personnel_data_all','lesson_course_teaching.LessonCourseTeaching/getPersonnelDataAll'); Route::get('personnel_data_all','lesson_course_teaching.LessonCourseTeaching/getPersonnelDataAll');
Route::get('test_paper','lesson_course_teaching.LessonCourseTeaching/getTestPaperList');
Route::put('binding_test_module/:id', 'lesson_course_teaching.LessonCourseTeaching/bindingTestModule');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

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

@ -39,6 +39,8 @@ Route::group('student', function () {
Route::get('label_all','student.Student/label_all'); Route::get('label_all','student.Student/label_all');
Route::get('getStudentByName','student.Student/getStudentByName');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,

161
niucloud/app/api/controller/apiController/Attendance.php

@ -0,0 +1,161 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\api\controller\apiController;
use app\Request;
use app\service\admin\campus\CampusService;
use app\service\api\apiService\AttendanceService;
use app\service\api\apiService\CommonService;
use core\base\BaseApiService;
/**
* 员工考勤控制器相关接口
* Class Personnel
* @package app\api\controller\apiController
*/
class Attendance extends BaseApiService
{
//列表
public function index(Request $request){
$where = [
'campus_id' => $request->param('campus_id', ''),//
'staff_id' => $request->param('staff_id', ''),//
'attendance_date' => $request->param('attendance_date', ''),//
'status' => $request->param('status', ''),//
'status_arr' => $request->param('status_arr', []),//
];
$res = (new AttendanceService())->getList($where);
return success($res);
}
//员工考勤-编辑(员工打卡/请假/签退)
public function edit(Request $request){
$campus_id =$request->param('campus_id','');//校区ID
$staff_id =$this->member_id;//人员ID
$attendance_date =$request->param('attendance_date','');//考勤日期
$remarks =$request->param('remarks','');//备注
$status =$request->param('status','');//考勤状态: present-出勤, absent-缺勤, late-迟到, leave_early-早退,leave-请假,sign_out-签退
$longitude = $request->param('longitude','');//经度
$latitude = $request->param('latitude','');//纬度
$coordinate = '';//坐标
if(!empty($longitude) && !empty($latitude)){
$coordinate = "{$longitude},{$latitude}";
}else{
return fail('缺少定位坐标');
}
if(empty($campus_id) || empty($attendance_date) || empty($status)){
return fail('缺少参数');
}
if(!in_array($status,['present','absent','late','leave_early','leave','sign_out'])){
return fail('状态类型不正确');
}
//获取校区的经纬度坐标
$campus_data = (new CampusService)->getInfo($campus_id);//查校区详情
$campus_lng = $campus_data['campus_coordinates']['lng'];//校区经度坐标
$campus_lat = $campus_data['campus_coordinates']['lat'];//校区纬度坐标
$radius = 1500.0;//打卡半径(米)
//判断签到+签退时是否超出打卡范围
if(in_array($status,['present','sign_out'])){
$isInCheckRange = (new CommonService())->isInCheckRange((float)$campus_lng, (float)$campus_lat, (float)$longitude, (float)$latitude, (float)$radius);
if(!$isInCheckRange){
return fail("超出打卡范围,规定打卡范围({$radius}米)");
}
}
//查询数据是否存在,一天一个校区同一个人只能产生一条数据
$date_h = date('H:i:s');
$obj = (new AttendanceService());
$info = $obj->info([
'campus_id'=>$campus_id,
'staff_id'=>$staff_id,
'attendance_date'=>$attendance_date,
])['data'];
//如果是签退的时候就是修改
if($status == 'sign_out'){
if (($info['status'] ?? '') != 'present') {
return fail('未查询到今日签到信息,无法签退');
}
//签退的情况
$where = [
'id'=>$info['id']
];
$data = [
'check_out_time'=>$date_h,//签退时间
'coordinate'=>$coordinate,//坐标|经度,纬度
];
//执行修改操作
$res = $obj->editData($where,$data);
}else{
//如果是签到/请假
$data = [
'campus_id'=>$campus_id,//校区ID
'staff_id'=>$staff_id,//人员ID
'attendance_date'=>$attendance_date,//考勤日期
'coordinate'=>$coordinate,//坐标|经度,纬度
'status'=>$status,//考勤状态
];
//如果是签到的情况
if($info){
if($status == 'present' && $info['status'] == 'present'){
//更新签到数据
//执行修改操作
$edit_data = [
'check_in_time'=>$date_h,//签到时间
'coordinate'=>$coordinate,//坐标|经度,纬度
'status'=>$status//考勤状态
];
$res = $obj->editData(['id'=>$info['id']],$edit_data);
if(!$res['code']){
return fail($res['msg']);
}
return success($res['data']);
}
$status_arr = (new \app\model\attendance\Attendance())::STATUS;
$status_name = $status_arr[$info['status']] ?? '';
if(!$status_name){
$status_name = '考勤';
}
return fail("今日已{$status_name}重复操作");
}else{
$data['check_in_time'] = $date_h;//签到时间
if($remarks){
$data['remarks'] = $remarks;
}
//执行创建操作
$res = $obj->addData($data);
if(!$res['code']){
return fail($res['msg']);
}
return success($res['data']);
}
}
}
}

7
niucloud/app/api/route/route.php

@ -233,7 +233,12 @@ Route::group(function () {
Route::post('communicationRecords/add', 'apiController.CommunicationRecords/add'); Route::post('communicationRecords/add', 'apiController.CommunicationRecords/add');
//校区-获取员工下的全部校区 //校区-获取员工下的全部校区
Route::get('campus/getPersonnelCampus', 'apiController.campus/getPersonnelCampus'); Route::get('campus/getPersonnelCampus', 'apiController.Campus/getPersonnelCampus');
//员工考勤-列表(员工打卡/请假/签退)
Route::post('attendance/index', 'apiController.Attendance/index');
//员工考勤-编辑(员工打卡/请假/签退)
Route::post('attendance/edit', 'apiController.Attendance/edit');

11
niucloud/app/common.php

@ -1087,7 +1087,8 @@ function getEmployeeNumber()
return date('Ymd') . $max_id; return date('Ymd') . $max_id;
} }
function return_pay_config($campus_id,$order_id){ function return_pay_config($campus_id, $order_id)
{
$campus_pay = new \app\model\campus_pay\CampusPay(); $campus_pay = new \app\model\campus_pay\CampusPay();
$pay_config = $campus_pay->where(['campus_id' => $campus_id])->find(); $pay_config = $campus_pay->where(['campus_id' => $campus_id])->find();
@ -1095,6 +1096,10 @@ function return_pay_config($campus_id,$order_id){
$vx_config = $sysConfig->where(['config_key' => 'WECHAT'])->value("value"); $vx_config = $sysConfig->where(['config_key' => 'WECHAT'])->value("value");
if (!$vx_config || !$pay_config) {
throw new \Exception('当前校区支付配置不存在');
}
$config = [ $config = [
// 必填-商户号 // 必填-商户号
'mch_id' => $pay_config['mchid'], 'mch_id' => $pay_config['mchid'],
@ -1103,7 +1108,7 @@ function return_pay_config($campus_id,$order_id){
// 必填-商户公钥证书路径 // 必填-商户公钥证书路径
'mch_public_cert_path' => $pay_config['apiclient_key'], 'mch_public_cert_path' => $pay_config['apiclient_key'],
// 必填 // 必填
'notify_url' => 'https://zh.hnhbty.cn/api/pay/qrcodenotify/order_id/'.$order_id, 'notify_url' => 'https://zh.hnhbty.cn/api/pay/qrcodenotify/order_id/' . $order_id,
// 选填-公众号 的 app_id // 选填-公众号 的 app_id
'mp_app_id' => $vx_config['app_id'], 'mp_app_id' => $vx_config['app_id'],
// 选填-小程序 的 app_id // 选填-小程序 的 app_id
@ -1121,7 +1126,7 @@ function getCurrentDomain()
$host = $_SERVER['HTTP_HOST']; // 包括域名和端口(如存在) $host = $_SERVER['HTTP_HOST']; // 包括域名和端口(如存在)
return $scheme . $host.'/'; return $scheme . $host . '/';
} }
function decryptWechatPayNotify($ciphertext, $nonce, $associatedData, $key) function decryptWechatPayNotify($ciphertext, $nonce, $associatedData, $key)

39
niucloud/app/model/attendance/Attendance.php

@ -11,6 +11,7 @@
namespace app\model\attendance; namespace app\model\attendance;
use app\model\dict\Dict;
use core\base\BaseModel; use core\base\BaseModel;
use think\model\concern\SoftDelete; use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
@ -42,7 +43,14 @@ class Attendance extends BaseModel
*/ */
protected $name = 'attendance'; protected $name = 'attendance';
//状态类型
const STATUS = [
'present'=>'出勤',
'absent'=>'缺勤',
'late'=>'迟到',
'leave_early'=>'早退',
'leave'=>'请假'
];
@ -93,6 +101,35 @@ class Attendance extends BaseModel
$query->where("status", $value); $query->where("status", $value);
} }
} }
/**
* 获取考勤状态状态类型名称
* @param $value
* @param $data
* @return array|mixed|string
*/
public function getStatusNameAttr($value, $data)
{
$key = 'kq_status';
$val = (String)$data['status'];
if ((!empty($val) || isset($val)) && $val !== '') {
$dict = Dict::where('key',$key)->find();
$dictionary = $dict['dictionary'] ?? [];
// 查找匹配的 name
$res = '';
foreach ($dictionary as $item) {
if ($item['value'] == $val) {
$res = $item['name'];
break;
}
}
return $res;
} else {
return '';
}
}

91
niucloud/app/model/chat_friends/ChatFriends.php

@ -0,0 +1,91 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\model\chat_friends;
use app\model\customer_resources\CustomerResources;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use app\model\personnel\Personnel;
/**
* 员工与会员聊天好友关系模型
* Class Attendance
* @package app\model\attendance
*/
class ChatFriends extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'chat_friends';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'deleted_at';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/**
* 搜索器:员工人力资源表id
* @param $value
* @param $data
*/
public function searchPersonnelIdAttr($query, $value, $data)
{
if ($value) {
$query->where("personnel_id", $value);
}
}
/**
* 搜索器:学生资源表id
* @param $value
* @param $data
*/
public function searchCustomerResourcesIdAttr($query, $value, $data)
{
if ($value) {
$query->where("customer_resources_id", $value);
}
}
public function campus(){
return $this->hasOne(CustomerResources::class, 'id', 'customer_resources_id')->joinType('left')->withField('name,id')->bind(['customer_resources_id'=>'name']);
}
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'personnel_id')->joinType('left')->withField('name,id')->bind(['personnel_id_name'=>'name']);
}
}

50
niucloud/app/model/chat_messages/ChatMessages.php

@ -0,0 +1,50 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\model\chat_messages;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
/**
* 员工与会员聊天记录表模型
* Class Attendance
* @package app\model\attendance
*/
class ChatMessages extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'chat_messages';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'deleted_at';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
}

8
niucloud/app/model/class_grade/ClassGrade.php

@ -11,6 +11,8 @@
namespace app\model\class_grade; namespace app\model\class_grade;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
use core\base\BaseModel; use core\base\BaseModel;
use think\model\concern\SoftDelete; use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
@ -219,7 +221,13 @@ class ClassGrade extends BaseModel
} }
public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']);
}
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'head_coach')->joinType('left')->withField('name,id')->bind(['head_coach_name'=>'name']);
}

148
niucloud/app/model/classroom/Classroom.php

@ -1,148 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\model\classroom;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
/**
* 场地管理模型
* Class Classroom
* @package app\model\classroom
*/
class Classroom extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'class';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'deleted_at';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/**
* 搜索器:场地管理所属校区
* @param $value
* @param $data
*/
public function searchCampusIdAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_id", $value);
}
}
/**
* 搜索器:场地管理班级名称
* @param $value
* @param $data
*/
public function searchClassNameAttr($query, $value, $data)
{
if ($value) {
$query->where("class_name", $value);
}
}
/**
* 搜索器:场地管理主教练
* @param $value
* @param $data
*/
public function searchHeadCoachAttr($query, $value, $data)
{
if ($value) {
$query->where("head_coach", $value);
}
}
/**
* 搜索器:场地管理班级类型
* @param $value
* @param $data
*/
public function searchClassTypeAttr($query, $value, $data)
{
if ($value) {
$query->where("class_type", $value);
}
}
/**
* 搜索器:场地管理助教
* @param $value
* @param $data
*/
public function searchAssistantCoachAttr($query, $value, $data)
{
if ($value) {
$query->where("assistant_coach", $value);
}
}
/**
* 搜索器:场地管理创建时间
* @param $value
* @param $data
*/
public function searchCreatedAtAttr($query, $value, $data)
{
if ($value) {
$query->where("created_at", $value);
}
}
/**
* 搜索器:场地管理班级状态
* @param $value
* @param $data
*/
public function searchStatusAttr($query, $value, $data)
{
if ($value) {
$query->where("status", $value);
}
}
public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']);
}
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'head_coach')->joinType('left')->withField('name,id')->bind(['head_coach_name'=>'name']);
}
}

140
niucloud/app/model/course_schedule/CourseSchedule.php

@ -11,6 +11,9 @@
namespace app\model\course_schedule; namespace app\model\course_schedule;
use app\model\course\Course;
use app\model\personnel\Personnel;
use app\model\venue\Venue;
use core\base\BaseModel; use core\base\BaseModel;
use think\model\concern\SoftDelete; use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
@ -24,7 +27,7 @@ use think\model\relation\HasOne;
class CourseSchedule extends BaseModel class CourseSchedule extends BaseModel
{ {
use SoftDelete; use SoftDelete;
/** /**
* 数据表主键 * 数据表主键
@ -39,152 +42,61 @@ class CourseSchedule extends BaseModel
protected $name = 'course_schedule'; protected $name = 'course_schedule';
/** /**
* 定义软删除标记字段. * 定义软删除标记字段.
* @var string * @var string
*/ */
protected $deleteTime = 'deleted_at'; protected $deleteTime = 'deleted_at';
/** /**
* 定义软删除字段的默认值. * 定义软删除字段的默认值.
* @var int * @var int
*/ */
protected $defaultSoftDelete = 0; protected $defaultSoftDelete = 0;
/** /**
* 搜索器:课程安排课程安排编号 * 搜索器:场地校区
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:课程安排校区ID
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusIdAttr($query, $value, $data) public function searchCampusIdAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("campus_id", $value); $query->where("campus_id", $value);
} }
} }
/** /**
* 搜索器:课程安排场地ID * 搜索器:场地ID
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchVenueIdAttr($query, $value, $data) public function searchVenueIdAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("venue_id", $value); $query->where("venue_id", $value);
} }
} }
/**
* 搜索器:课程安排上课日期
* @param $value
* @param $data
*/
public function searchCourseDateAttr($query, $value, $data) public function searchCourseDateAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("course_date", $value); $query->where("course_date", $value);
} }
} }
/** public function venue()
* 搜索器:课程安排上课时段
* @param $value
* @param $data
*/
public function searchTimeSlotAttr($query, $value, $data)
{
if ($value) {
$query->where("time_slot", $value);
}
}
/**
* 搜索器:课程安排课程ID
* @param $value
* @param $data
*/
public function searchCourseIdAttr($query, $value, $data)
{
if ($value) {
$query->where("course_id", $value);
}
}
/**
* 搜索器:课程安排上课教练ID
* @param $value
* @param $data
*/
public function searchCoachIdAttr($query, $value, $data)
{
if ($value) {
$query->where("coach_id", $value);
}
}
/**
* 搜索器:课程安排参与人员列表
* @param $value
* @param $data
*/
public function searchParticipantsAttr($query, $value, $data)
{
if ($value) {
$query->where("participants", $value);
}
}
/**
* 搜索器:课程安排上课学生列表
* @param $value
* @param $data
*/
public function searchStudentIdsAttr($query, $value, $data)
{ {
if ($value) { return $this->hasOne(Venue::class, 'id', 'venue_id');
$query->where("student_ids", $value);
}
} }
/** public function coach()
* 搜索器:课程安排根据场地容量判断的可安排学员位置数量
* @param $value
* @param $data
*/
public function searchAvailableCapacityAttr($query, $value, $data)
{ {
if ($value) { return $this->hasOne(Personnel::class, 'id', 'coach_id');
$query->where("available_capacity", $value);
}
} }
/** public function course()
* 搜索器:课程安排课程状态:
* @param $value
* @param $data
*/
public function searchStatusAttr($query, $value, $data)
{ {
if ($value) { return $this->hasOne(Course::class, 'id', 'course_id');
$query->where("status", $value);
}
} }
} }

32
niucloud/app/model/rel/ClassPersonnelRel.php

@ -0,0 +1,32 @@
<?php
namespace app\model\rel;
use app\model\personnel\Personnel;
use app\model\student\Student;
use core\base\BaseModel;
class ClassPersonnelRel extends BaseModel
{
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'class_personnel_rel';
public function personnel()
{
return $this->hasOne(Personnel::class, 'id', 'personnel_id');
}
public function student()
{
return $this->hasOne(Student::class, 'id', 'source_id');
}
}

6
niucloud/app/model/student/Student.php

@ -141,15 +141,15 @@ class Student extends BaseModel
public function customerResources(){ public function customerResources(){
return $this->hasOne(CustomerResources::class, 'id', 'user_id')->joinType('left')->withField('name,id')->bind(['user_id_name'=>'name']); return $this->hasOne(CustomerResources::class, 'id', 'user_id');
} }
public function campus(){ public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']); return $this->hasOne(Campus::class, 'id', 'campus_id');
} }
public function classGrade(){ public function classGrade(){
return $this->hasOne(ClassGrade::class, 'id', 'class_id')->joinType('left')->withField('class_name,id')->bind(['class_id_name'=>'class_name']); return $this->hasOne(ClassGrade::class, 'id', 'class_id');
} }
} }

2
niucloud/app/model/venue/Venue.php

@ -142,7 +142,7 @@ class Venue extends BaseModel
public function campus(){ public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']); return $this->hasOne(Campus::class, 'id', 'campus_id');
} }
} }

48
niucloud/app/service/admin/classroom/ClassroomService.php

@ -11,10 +11,12 @@
namespace app\service\admin\classroom; namespace app\service\admin\classroom;
use app\model\classroom\Classroom; use app\model\class_grade\ClassGrade;
use app\model\campus\Campus; use app\model\campus\Campus;
use app\model\course_schedule\CourseSchedule;
use app\model\personnel\Personnel; use app\model\personnel\Personnel;
use app\model\rel\ClassPersonnelRel;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -28,7 +30,7 @@ class ClassroomService extends BaseAdminService
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
$this->model = new Classroom(); $this->model = new ClassGrade();
} }
/** /**
@ -41,7 +43,7 @@ class ClassroomService extends BaseAdminService
$field = 'id,campus_id,campus_name,class_name,head_coach,age_group,class_type,assistant_coach,created_at,updated_at,deleted_at,status,sort_order,remarks'; $field = 'id,campus_id,campus_name,class_name,head_coach,age_group,class_type,assistant_coach,created_at,updated_at,deleted_at,status,sort_order,remarks';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["campus_id","class_name","head_coach","class_type","assistant_coach","created_at","status"], $where)->with(['campus','personnel','personnel'])->field($field)->order($order); $search_model = $this->model->withSearch(["campus_id", "class_name", "head_coach", "class_type", "assistant_coach", "created_at", "status"], $where)->with(['campus', 'personnel', 'personnel'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -55,8 +57,8 @@ class ClassroomService extends BaseAdminService
{ {
$field = 'id,campus_id,campus_name,class_name,head_coach,age_group,class_type,assistant_coach,created_at,updated_at,deleted_at,status,sort_order,remarks'; $field = 'id,campus_id,campus_name,class_name,head_coach,age_group,class_type,assistant_coach,created_at,updated_at,deleted_at,status,sort_order,remarks';
$info = $this->model->field($field)->where([['id', "=", $id]])->with(['campus','personnel','personnel'])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['campus', 'personnel', 'personnel'])->findOrEmpty()->toArray();
$info['status'] = strval($info['status']); $info['status'] = strval($info['status']);
return $info; return $info;
} }
@ -97,22 +99,40 @@ class ClassroomService extends BaseAdminService
return $res; return $res;
} }
public function getCampusAll(){ public function getCampusAll()
$campusModel = new Campus(); {
return $campusModel->select()->toArray(); $campusModel = new Campus();
return $campusModel->select()->toArray();
} }
public function getPersonnelAll(){ public function getPersonnelAll()
$personnelModel = new Personnel(); {
return $personnelModel->select()->toArray(); $personnelModel = new Personnel();
return $personnelModel->select()->toArray();
} }
public function classroom_all() public function classroom_all()
{ {
$data = $this->model->where('status', 1)->order('sort_order desc')->select()->toArray(); $data = $this->model->where('status', 1)->order('sort_order desc')->select()->toArray();
return $data; return $data;
} }
public function getClassroompeople($class_id)
{
$listmodel = new ClassPersonnelRel();
return $listmodel->with(['student','personnel'])->where('class_id', $class_id)->select()->toArray();
}
/**
* 获取班级当前时段人员安排情况
*/
public function getClassroompeopleCount($venue_id,$where = [])
{
$listmodel = new CourseSchedule();
return $listmodel->where('venue_id', $venue_id)
->where($where)
->select()
->toArray();
}
} }

10
niucloud/app/service/admin/course/CourseService.php

@ -101,6 +101,14 @@ class CourseService extends BaseAdminService
return $res; return $res;
} }
/**
* 获取课程列表
*/
public function getAllCourseList($where)
{
$field = 'id,course_name';
$where = array_filter($where);
return $this->model->where($where)->field($field)->select()->toArray();
}
} }

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

@ -13,6 +13,7 @@ namespace app\service\admin\course_schedule;
use app\model\course_schedule\CourseSchedule; use app\model\course_schedule\CourseSchedule;
use app\model\person_course_schedule\PersonCourseSchedule; use app\model\person_course_schedule\PersonCourseSchedule;
use app\service\admin\venue\VenueService;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -36,10 +37,13 @@ class CourseScheduleService extends BaseAdminService
*/ */
public function getPage(array $where = []) public function getPage(array $where = [])
{ {
$field = 'id,campus_id,venue_id,course_date,time_slot,course_id,coach_id,participants,student_ids,available_capacity,status,created_by,created_at,updated_at,deleted_at'; $field = 'id,campus_id,venue_id,course_date,time_slot,course_id,coach_id,participants,student_ids,available_capacity,status,created_by,created_at,updated_at,deleted_at,is_system_add';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","campus_id","venue_id","course_date","time_slot","course_id","coach_id","participants","student_ids","available_capacity","status"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["campus_id", "venue_id", "course_date"], $where)
->with(['venue', 'coach', 'course'])
->field($field)
->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -51,7 +55,7 @@ class CourseScheduleService extends BaseAdminService
*/ */
public function getInfo(int $id) public function getInfo(int $id)
{ {
$field = 'id,campus_id,venue_id,course_date,time_slot,course_id,coach_id,participants,student_ids,available_capacity,status,created_by,created_at,updated_at,deleted_at'; $field = 'id,campus_id,venue_id,course_date,time_slot,course_id,coach_id,participants,student_ids,available_capacity,status,created_by,created_at,updated_at,deleted_at,is_system_add';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray();
return $info; return $info;
@ -64,9 +68,29 @@ class CourseScheduleService extends BaseAdminService
*/ */
public function add(array $data) public function add(array $data)
{ {
$res = $this->model->create($data); $create = [
return $res->id; 'campus_id' => $data['campus_id'],
'venue_id' => $data['venue_id'],
'course_date' => $data['course_date'],
'time_slot' => $data['time_slot'],
'course_id' => $data['course_id'],
'coach_id' => $data['coach_id'],
'participants' => $data['participants'] ? $data['participants'] : [],
'student_ids' => $data['student_ids'] ? $data['student_ids'] : [],
'is_system_add' => $data['is_system_add']
];
$status = $this->model->where([
['course_date', '=', $data['course_date']],
['time_slot', '=', $data['time_slot']],
['campus_id', '=', $data['campus_id']],
['venue_id', '=', $data['venue_id']]
])->find();
if ($status) {
throw new \Exception('该时间段已有课程安排');
}
$res = $this->model->create($create);
return $res->id;
} }
/** /**
@ -77,8 +101,29 @@ class CourseScheduleService extends BaseAdminService
*/ */
public function edit(int $id, array $data) public function edit(int $id, array $data)
{ {
$create = [
'campus_id' => $data['campus_id'],
'venue_id' => $data['venue_id'],
'course_date' => $data['course_date'],
'time_slot' => $data['time_slot'],
'course_id' => $data['course_id'],
'coach_id' => $data['coach_id'],
'participants' => $data['participants'] ? $data['participants'] : [],
'student_ids' => $data['student_ids'] ? $data['student_ids'] : [],
'is_system_add' => $data['is_system_add']
];
$status = $this->model->where([
['course_date', '=', $data['course_date']],
['time_slot', '=', $data['time_slot']],
['campus_id', '=', $data['campus_id']],
['venue_id', '=', $data['venue_id']],
['id', '<>', $id]
])->find();
$this->model->where([['id', '=', $id]])->update($data); if ($status) {
throw new \Exception('该时间段已有课程安排');
}
$this->model->where([['id', '=', $id]])->update($create);
return true; return true;
} }
@ -104,70 +149,73 @@ class CourseScheduleService extends BaseAdminService
// 获取日期范围,默认为本周 // 获取日期范围,默认为本周
$start_date = $where['start_date'] ?? date('Y-m-d', strtotime('monday this week')); $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')); $end_date = $where['end_date'] ?? date('Y-m-d', strtotime('sunday this week'));
// 校区ID // 校区ID
$campus_id = isset($where['campus_id']) && !empty($where['campus_id']) ? intval($where['campus_id']) : 0; $campus_id = isset($where['campus_id']) && !empty($where['campus_id']) ? intval($where['campus_id']) : 0;
// 查询条件 // 查询条件
$query_condition = [ $query_condition = [
['course_date', '>=', $start_date], ['course_date', '>=', $start_date],
['course_date', '<=', $end_date] ['course_date', '<=', $end_date]
]; ];
// 如果指定了校区,添加校区筛选条件 // 如果指定了校区,添加校区筛选条件
if ($campus_id > 0) { if ($campus_id > 0) {
$query_condition[] = ['campus_id', '=', $campus_id]; $query_condition[] = ['campus_id', '=', $campus_id];
} }
// 查询指定日期范围内的课程安排 // 查询指定日期范围内的课程安排
$schedules = $this->model->where($query_condition)->select()->toArray(); $schedules = $this->model->where($query_condition)->select()->toArray();
// 获取所有相关的人员课程安排关系 // 获取所有相关的人员课程安排关系
$schedule_ids = array_column($schedules, 'id'); $schedule_ids = array_column($schedules, 'id');
$person_schedules = []; $person_schedules = [];
if (!empty($schedule_ids)) { if (!empty($schedule_ids)) {
$person_schedules = (new PersonCourseSchedule())->where([ $person_schedules = (new PersonCourseSchedule())->where([
['schedule_id', 'in', $schedule_ids] ['schedule_id', 'in', $schedule_ids]
])->select()->toArray(); ])->select()->toArray();
} }
// 组织数据结构 // 组织数据结构
$days = []; $days = [];
$weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; $weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
// 获取所有时间段和教室 // 获取所有时间段和教室
$time_slots = []; $time_slots = [];
$classrooms = []; $classrooms = [];
// 如果没有数据,设置默认的时间段和教室 // 如果没有数据,设置默认的时间段和教室
if (empty($schedules)) { 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 $venues = (new VenueService())->getVenueAll($campus_id);
$time_slots = (new VenueService())->getVenueTime($venues);
$classrooms = $venues;
} else { } else {
foreach ($schedules as $schedule) { foreach ($schedules as $schedule) {
if (!in_array($schedule['time_slot'], $time_slots)) { if (!in_array($schedule['time_slot'], $time_slots)) {
$time_slots[] = $schedule['time_slot']; $time_slots[] = $schedule['time_slot'];
} }
// 假设venue_id代表教室 // 假设venue_id代表教室
if (!in_array($schedule['venue_id'], $classrooms)) { if (!in_array($schedule['venue_id'], $classrooms)) {
$classrooms[] = $schedule['venue_id']; $classrooms[] = $schedule['venue_id'];
} }
} }
} }
// 按日期组织数据 // 按日期组织数据
$current_date = $start_date; $current_date = $start_date;
$day_index = (int)date('N', strtotime($current_date)) - 1; // 获取周几(1-7),转为索引(0-6) $day_index = (int)date('N', strtotime($current_date)) - 1; // 获取周几(1-7),转为索引(0-6)
while (strtotime($current_date) <= strtotime($end_date)) { while (strtotime($current_date) <= strtotime($end_date)) {
$day_schedules = array_filter($schedules, function($schedule) use ($current_date) { $day_schedules = array_filter($schedules, function ($schedule) use ($current_date) {
return $schedule['course_date'] == $current_date; return $schedule['course_date'] == $current_date;
}); });
$day_classrooms = empty($day_schedules) ? $classrooms : []; $day_classrooms = empty($day_schedules) ? $classrooms : [];
if (!empty($day_schedules)) { if (!empty($day_schedules)) {
foreach ($day_schedules as $schedule) { foreach ($day_schedules as $schedule) {
if (!in_array($schedule['venue_id'], $day_classrooms)) { if (!in_array($schedule['venue_id'], $day_classrooms)) {
@ -175,7 +223,7 @@ class CourseScheduleService extends BaseAdminService
} }
} }
} }
// 构建每个时间段的数据 // 构建每个时间段的数据
$day_time_slots = []; $day_time_slots = [];
foreach ($time_slots as $time_slot) { foreach ($time_slots as $time_slot) {
@ -183,22 +231,22 @@ class CourseScheduleService extends BaseAdminService
'timeRange' => $time_slot, 'timeRange' => $time_slot,
'color' => '#' . dechex(rand(0x000000, 0xFFFFFF)), // 随机颜色 'color' => '#' . dechex(rand(0x000000, 0xFFFFFF)), // 随机颜色
]; ];
// 查找该时间段的课程 // 查找该时间段的课程
$slot_schedule = array_filter($day_schedules, function($schedule) use ($time_slot) { $slot_schedule = array_filter($day_schedules, function ($schedule) use ($time_slot) {
return $schedule['time_slot'] == $time_slot; return $schedule['time_slot'] == $time_slot;
}); });
if (!empty($slot_schedule)) { if (!empty($slot_schedule)) {
$schedule = reset($slot_schedule); $schedule = reset($slot_schedule);
// 查找该课程的学员 // 查找该课程的学员
$students = array_filter($person_schedules, function($person) use ($schedule) { $students = array_filter($person_schedules, function ($person) use ($schedule) {
return $person['schedule_id'] == $schedule['id']; return $person['schedule_id'] == $schedule['id'];
}); });
$student_names = array_column($students, 'person_id'); $student_names = array_column($students, 'person_id');
$slot_data['course'] = [ $slot_data['course'] = [
'teacher' => $schedule['coach_id'] ?? '', 'teacher' => $schedule['coach_id'] ?? '',
'students' => $student_names, 'students' => $student_names,
@ -206,74 +254,32 @@ class CourseScheduleService extends BaseAdminService
'hasnumber' => $schedule['available_capacity'] ?? 0, 'hasnumber' => $schedule['available_capacity'] ?? 0,
]; ];
} }
$day_time_slots[] = $slot_data; $day_time_slots[] = $slot_data;
} }
$days[] = [ $days[] = [
'date' => $weekdays[$day_index % 7] . ' (' . $current_date . ')', 'date' => $weekdays[$day_index % 7] . ' (' . $current_date . ')',
'timeSlots' => $day_time_slots, 'timeSlots' => $day_time_slots,
'classrooms' => $day_classrooms, 'classrooms' => $day_classrooms,
]; ];
$current_date = date('Y-m-d', strtotime($current_date . ' +1 day')); $current_date = date('Y-m-d', strtotime($current_date . ' +1 day'));
$day_index = ($day_index + 1) % 7; $day_index = ($day_index + 1) % 7;
} }
return $days; return $days;
} }
/** /**
* 自动创建课程安排记录 * @param $id
* @param string $start_date 开始日期 * @return CourseSchedule[]|array|\think\Collection
* @param string $end_date 结束日期 * @throws \think\db\exception\DataNotFoundException
* @param int $campus_id 校区ID * @throws \think\db\exception\DbException
* @return array 创建的课程安排记录 * @throws \think\db\exception\ModelNotFoundException
*/ */
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;
}
public function getCampusVenue($id) public function getCampusVenue($id)
{ {
return $this->model->where('availability_status', 1)->where('campus_id',$id)->select(); return $this->model->where('availability_status', 1)->where('campus_id', $id)->select();
} }
} }

139
niucloud/app/service/admin/customer_resources/CustomerResourcesService.php

@ -11,6 +11,7 @@
namespace app\service\admin\customer_resources; namespace app\service\admin\customer_resources;
use app\model\campus_person_role\CampusPersonRole;
use app\model\customer_resource_changes\CustomerResourceChanges; use app\model\customer_resource_changes\CustomerResourceChanges;
use app\model\customer_resources\CustomerResources; use app\model\customer_resources\CustomerResources;
use app\model\personnel\Personnel; use app\model\personnel\Personnel;
@ -47,44 +48,44 @@ class CustomerResourcesService extends BaseAdminService
$field = 'a.*,b.id as shared_id'; $field = 'a.*,b.id as shared_id';
$order = 'a.id desc'; $order = 'a.id desc';
$where = []; $where = [];
if($data['phone_number']){ if ($data['phone_number']) {
$where[] = ['a.phone_number','=',$data['phone_number']]; $where[] = ['a.phone_number', '=', $data['phone_number']];
} }
if($data['name']){ if ($data['name']) {
$where[] = ['a.name','=',$data['name']]; $where[] = ['a.name', '=', $data['name']];
} }
if($data['age']){ if ($data['age']) {
$where[] = ['a.age','=',$data['age']]; $where[] = ['a.age', '=', $data['age']];
} }
if($data['gender']){ if ($data['gender']) {
$where[] = ['a.gender','=',$data['gender']]; $where[] = ['a.gender', '=', $data['gender']];
} }
if($data['type'] == 'yjfp'){ if ($data['type'] == 'yjfp') {
$where[] = ['b.shared_by','=',0]; $where[] = ['b.shared_by', '=', 0];
} }
if($data['member_label']){ if ($data['member_label']) {
$where[] = ['a.member_label','like',"%".$data['member_label']."%"]; $where[] = ['a.member_label', 'like', "%" . $data['member_label'] . "%"];
} }
$search_model = $this->model $search_model = $this->model
->alias("a") ->alias("a")
->join(['school_resource_sharing' => 'b'],'a.id = b.resource_id','left') ->join(['school_resource_sharing' => 'b'], 'a.id = b.resource_id', 'left')
->where($where) ->where($where)
->with(['personnel'])->field($field)->order($order); ->with(['personnel'])->field($field)->order($order);
if (isset($data['created_at'][0]) && isset($data['created_at'][1])) { if (isset($data['created_at'][0]) && isset($data['created_at'][1])) {
$search_model->whereBetweenTime('created_at', $data['created_at'][0]."00:00:00", $data['created_at'][1]."23:59:59"); $search_model->whereBetweenTime('created_at', $data['created_at'][0] . "00:00:00", $data['created_at'][1] . "23:59:59");
} }
if (isset($data['updated_at'][0]) && isset($data['updated_at'][1])) { if (isset($data['updated_at'][0]) && isset($data['updated_at'][1])) {
$search_model->whereBetweenTime('updated_at', $data['updated_at'][0]."00:00:00", $data['updated_at'][1]."23:59:59"); $search_model->whereBetweenTime('updated_at', $data['updated_at'][0] . "00:00:00", $data['updated_at'][1] . "23:59:59");
} }
@ -106,15 +107,16 @@ class CustomerResourcesService extends BaseAdminService
$sixSpeed = new SixSpeed(); $sixSpeed = new SixSpeed();
$data = $sixSpeed->where(['resource_id' => $id])->field("*,distance as distance_tow")->findOrEmpty()->toArray(); $data = $sixSpeed->where(['resource_id' => $id])->field("*,distance as distance_tow")->findOrEmpty()->toArray();
$info = $info+$data; $info = $info + $data;
return $this->makeUp($info); return $this->makeUp($info);
} }
public function makeUp($data){ public function makeUp($data)
{
//会员标签 //会员标签
if(!empty($data['member_label'])){ if (!empty($data['member_label'])) {
$data['member_label_array'] = (new MemberLabelService())->getMemberLabelListByLabelIds($data['member_label']); $data['member_label_array'] = (new MemberLabelService())->getMemberLabelListByLabelIds($data['member_label']);
} }
return $data; return $data;
@ -131,7 +133,7 @@ class CustomerResourcesService extends BaseAdminService
$personnel = new Personnel(); $personnel = new Personnel();
$data['consultant'] = $personnel->where(['sys_user_id' => $this->uid])->value("id"); $data['consultant'] = $personnel->where(['sys_user_id' => $this->uid])->value("id");
if(!$data['consultant']){ if (!$data['consultant']) {
return fail("操作失败"); return fail("操作失败");
} }
@ -142,15 +144,15 @@ class CustomerResourcesService extends BaseAdminService
$res = $this->model->create($data); $res = $this->model->create($data);
$role_id = $personnel->alias("a")->join(['school_campus_person_role' => 'b'],'a.id = b.person_id','left') $role_id = $personnel->alias("a")->join(['school_campus_person_role' => 'b'], 'a.id = b.person_id', 'left')
->where(['a.id' => $data['consultant']])->value('b.role_id'); ->where(['a.id' => $data['consultant']])->value('b.role_id');
$resourceSharing->insert([ $resourceSharing->insert([
'resource_id' => $res->id, 'resource_id' => $res->id,
'user_id' => $data['consultant'], 'user_id' => $data['consultant'],
'role_id' => $role_id 'role_id' => $role_id
]); ]);
if($data['purchase_power']){ if ($data['purchase_power']) {
$six_id = $sixSpeed->where(['resource_id' => $res->id])->value("id"); $six_id = $sixSpeed->where(['resource_id' => $res->id])->value("id");
$data['staff_id'] = $data['consultant']; $data['staff_id'] = $data['consultant'];
@ -170,9 +172,9 @@ class CustomerResourcesService extends BaseAdminService
'staff_id' => $data['staff_id'], 'staff_id' => $data['staff_id'],
'resource_id' => $res->id 'resource_id' => $res->id
]; ];
if($six_id){ if ($six_id) {
$sixSpeed->where(['resource_id' => $res->id])->update($field); $sixSpeed->where(['resource_id' => $res->id])->update($field);
}else{ } else {
$sixSpeed->insert($field); $sixSpeed->insert($field);
} }
} }
@ -218,14 +220,14 @@ class CustomerResourcesService extends BaseAdminService
'create_date' => $data['create_date'] 'create_date' => $data['create_date']
]); ]);
$resources_save = getModifiedFields($res,$data); $resources_save = getModifiedFields($res, $data);
$customerResourceChanges = new CustomerResourceChanges(); $customerResourceChanges = new CustomerResourceChanges();
if($resources_save['is_save']){ if ($resources_save['is_save']) {
$customerResourceChanges->insert([ $customerResourceChanges->insert([
'customer_resource_id' => $id, 'customer_resource_id' => $id,
'operator_id' => $res['consultant'], 'operator_id' => $res['consultant'],
'campus_id' => $data['campus'], 'campus_id' => $data['campus'],
'modified_fields' => $resources_save['modified_fields'], 'modified_fields' => $resources_save['modified_fields'],
'old_values' => $resources_save['old_values'], 'old_values' => $resources_save['old_values'],
'new_values' => $resources_save['new_values'] 'new_values' => $resources_save['new_values']
@ -234,13 +236,12 @@ class CustomerResourcesService extends BaseAdminService
$sixSpeed = new SixSpeed(); $sixSpeed = new SixSpeed();
if($data['purchase_power']){ if ($data['purchase_power']) {
$sixSpeedModificationLog = new SixSpeedModificationLog(); $sixSpeedModificationLog = new SixSpeedModificationLog();
$six_id = $sixSpeed->where(['resource_id' => $id])->value("id"); $six_id = $sixSpeed->where(['resource_id' => $id])->value("id");
$data['staff_id'] = $res['consultant']; $data['staff_id'] = $res['consultant'];
$field = [ $field = [
'purchase_power' => $data['purchase_power'], 'purchase_power' => $data['purchase_power'],
'concept_awareness' => $data['concept_awareness'], 'concept_awareness' => $data['concept_awareness'],
@ -256,16 +257,16 @@ class CustomerResourcesService extends BaseAdminService
'staff_id' => $data['staff_id'], 'staff_id' => $data['staff_id'],
'resource_id' => $id 'resource_id' => $id
]; ];
if($six_id){ if ($six_id) {
$six_log = $sixSpeed->where(['resource_id' => $id])->findOrEmpty()->toArray(); $six_log = $sixSpeed->where(['resource_id' => $id])->findOrEmpty()->toArray();
$six_save = getModifiedFields($six_log,$field); $six_save = getModifiedFields($six_log, $field);
if($six_save['is_save']){ if ($six_save['is_save']) {
$sixSpeedModificationLog->insert([ $sixSpeedModificationLog->insert([
'customer_resource_id' => $id, 'customer_resource_id' => $id,
'operator_id' => $res['consultant'], 'operator_id' => $res['consultant'],
'campus_id' => $data['campus'], 'campus_id' => $data['campus'],
'modified_field' => $six_save['modified_fields'], 'modified_field' => $six_save['modified_fields'],
'old_value' => $six_save['old_values'], 'old_value' => $six_save['old_values'],
'new_value' => $six_save['new_values'] 'new_value' => $six_save['new_values']
@ -273,7 +274,7 @@ class CustomerResourcesService extends BaseAdminService
} }
$sixSpeed->where(['resource_id' => $id])->update($field); $sixSpeed->where(['resource_id' => $id])->update($field);
}else{ } else {
$sixSpeed->insert($field); $sixSpeed->insert($field);
} }
} }
@ -294,26 +295,29 @@ class CustomerResourcesService extends BaseAdminService
} }
public function getPersonnelAll($data){ public function getPersonnelAll($data)
$personnelModel = new Personnel(); {
$where = []; $personnelModel = new Personnel();
if($data['role_id']){ $where = [];
$where[] = ['b.role_id','=',$data['role_id']]; if ($data['role_id']) {
} $where[] = ['b.role_id', '=', $data['role_id']];
return $personnelModel }
->alias("a") return $personnelModel
->join(['school_campus_person_role' => 'b'],'a.id = b.person_id','left') ->alias("a")
->field("a.*") ->join(['school_campus_person_role' => 'b'], 'a.id = b.person_id', 'left')
->where($where)->select()->toArray(); ->field("a.*")
->where($where)->select()->toArray();
} }
public function getCampusAll(){ public function getCampusAll()
$campusModel = new Campus(); {
return $campusModel->select()->toArray(); $campusModel = new Campus();
return $campusModel->select()->toArray();
} }
public function fp_edit($data){ public function fp_edit($data)
{
$resourceSharing = new ResourceSharing(); $resourceSharing = new ResourceSharing();
$resourceSharing->where(['id' => $data['shared_id']])->update([ $resourceSharing->where(['id' => $data['shared_id']])->update([
'shared_by' => $data['shared_by'], 'shared_by' => $data['shared_by'],
@ -322,5 +326,42 @@ class CustomerResourcesService extends BaseAdminService
return "分配成功"; return "分配成功";
} }
public function personnelAllByname($name)
{
return $this->model->where('name', 'like', '%' . $name . '%')->select()->toArray();
}
/**
* 获取销售人员
*/
public function getSalesPerson($campus_id)
{
}
/**
* 获取市场人员
*/
public function getMarketPerson($campus_id)
{
}
/**
* 获取教练人员
*/
public function getCoachPerson($campus_id)
{
$coachModel = new CampusPersonRole();
return $coachModel
->alias("a")
->join(['school_personnel' => 'b'], 'a.person_id = b.id', 'left')
->field("a.*,b.name,b.phone")
->where([
['a.campus_id', '=', $campus_id],
['a.role_id', '=', 5]
])->select()->toArray();
}
} }

22
niucloud/app/service/admin/lesson_course_teaching/LessonCourseTeachingService.php

@ -13,6 +13,7 @@ namespace app\service\admin\lesson_course_teaching;
use app\model\lesson_course_teaching\LessonCourseTeaching; use app\model\lesson_course_teaching\LessonCourseTeaching;
use app\model\personnel_data\PersonnelData; use app\model\personnel_data\PersonnelData;
use app\model\exam_papers\ExamPapers;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -644,9 +645,28 @@ class LessonCourseTeachingService extends BaseAdminService
$search_model = $personnelDataModel->where('is_sys_user', 1)->where($whereArr)->field($field)->order($order); $search_model = $personnelDataModel->where('is_sys_user', 1)->where($whereArr)->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
}
public function getTestPaperList(array $where = []){
$ExamPapersModel = new ExamPapers();
$field = 'id,selection_mode,total_score,passing_score,created_at';
$order = 'id desc';
$whereArr = [];
if (!empty($where['total_score'])) {
$whereArr[] = ['total_score','=',$where['total_score']];
}
if (!empty($where['selection_mode'])) {
$whereArr[] = ['selection_mode','=',$where['selection_mode']];
}
$search_model = $ExamPapersModel->where($whereArr)->field($field)->order($order);
$list = $this->pageQuery($search_model);
return $list;
} }
public function bindingTestModule(int $id, array $data)
{
$this->model->where([['id', '=', $id]])->update($data);
return true;
}
} }

1
niucloud/app/service/admin/pay/PayService.php

@ -241,6 +241,7 @@ class PayService extends BaseAdminService
$pay = new PayLoader('Wechatpay', $config); $pay = new PayLoader('Wechatpay', $config);
$url = $pay->scan($params); $url = $pay->scan($params);
$path = qrcode($url['code_url'],'',[],'upload/qrcode/pay/'); $path = qrcode($url['code_url'],'',[],'upload/qrcode/pay/');
$order->where(['id' => $data['order_id']])->update(['payment_id' => $out_trade_no,'ipv3' => $config['mch_secret_key']]); $order->where(['id' => $data['order_id']])->update(['payment_id' => $out_trade_no,'ipv3' => $config['mch_secret_key']]);

45
niucloud/app/service/admin/student/StudentService.php

@ -44,7 +44,7 @@ class StudentService extends BaseAdminService
$field = 'id,user_id,campus_id,class_id,name,gender,age,birthday,member_label,emergency_contact,contact_phone,note,status,created_at,updated_at,deleted_at'; $field = 'id,user_id,campus_id,class_id,name,gender,age,birthday,member_label,emergency_contact,contact_phone,note,status,created_at,updated_at,deleted_at';
$order = 'id asc'; $order = 'id asc';
$search_model = $this->model->withSearch(["campus_id","name","emergency_contact","contact_phone","created_at","member_label"], $where)->with(['customerResources','campus','classGrade'])->field($field)->order($order); $search_model = $this->model->withSearch(["campus_id", "name", "emergency_contact", "contact_phone", "created_at", "member_label"], $where)->with(['customerResources', 'campus', 'classGrade'])->field($field)->order($order);
return $this->pageQuery($search_model, function ($item, $key) { return $this->pageQuery($search_model, function ($item, $key) {
$item = $this->makeUp($item); $item = $this->makeUp($item);
}); });
@ -59,7 +59,7 @@ class StudentService extends BaseAdminService
{ {
$field = 'id,user_id,campus_id,class_id,name,gender,age,birthday,emergency_contact,member_label,contact_phone,note,status,created_at,updated_at,deleted_at'; $field = 'id,user_id,campus_id,class_id,name,gender,age,birthday,emergency_contact,member_label,contact_phone,note,status,created_at,updated_at,deleted_at';
$info = $this->makeUp($this->model->field($field)->where([['id', "=", $id]])->with(['customerResources','campus','classGrade'])->findOrEmpty()->toArray()); $info = $this->makeUp($this->model->field($field)->where([['id', "=", $id]])->with(['customerResources', 'campus', 'classGrade'])->findOrEmpty()->toArray());
return $info; return $info;
} }
@ -68,9 +68,10 @@ class StudentService extends BaseAdminService
* 组合整理数据 * 组合整理数据
* @param $data * @param $data
*/ */
public function makeUp($data){ public function makeUp($data)
{
//会员标签 //会员标签
if(!empty($data['member_label'])){ if (!empty($data['member_label'])) {
$data['member_label_array'] = (new StudentLabelService())->getMemberLabelListByLabelIds($data['member_label']); $data['member_label_array'] = (new StudentLabelService())->getMemberLabelListByLabelIds($data['member_label']);
} }
return $data; return $data;
@ -114,26 +115,40 @@ class StudentService extends BaseAdminService
} }
public function getCustomerResourcesAll(){ public function getCustomerResourcesAll()
$customerResourcesModel = new CustomerResources(); {
return $customerResourcesModel->select()->toArray(); $customerResourcesModel = new CustomerResources();
return $customerResourcesModel->select()->toArray();
} }
public function getCampusAll(){ public function getCampusAll()
$campusModel = new Campus(); {
return $campusModel->select()->toArray(); $campusModel = new Campus();
return $campusModel->select()->toArray();
} }
public function getClassGradeAll(){ public function getClassGradeAll()
$classGradeModel = new ClassGrade(); {
return $classGradeModel->select()->toArray(); $classGradeModel = new ClassGrade();
return $classGradeModel->select()->toArray();
} }
public function label_all(){ public function label_all()
{
$field = 'label_id, label_name'; $field = 'label_id, label_name';
return (new StudentLabel())->where([ ['label_id', '>', 0] ])->field($field)->order('sort desc,create_time desc')->select()->toArray(); return (new StudentLabel())->where([['label_id', '>', 0]])->field($field)->order('sort desc,create_time desc')->select()->toArray();
} }
public function getStudentByName($name)
{
$query = $this->model->with(['customer_resources'])->where([['name', 'like', '%' . $name . '%']]);
$list = $query->select()->toArray();
foreach ($list as &$item) {
$item['client_name'] = $item['name'] . '(家长:' . $item['customer_resources']['name'] . ' 电话:' . $item['customer_resources']['phone_number'] . ')';
}
return $list;
}
} }

99
niucloud/app/service/admin/venue/VenueService.php

@ -137,34 +137,83 @@ class VenueService extends BaseAdminService
/** /**
* 获取场地那天的可预约时间 * 获取场地那天的可预约时间
*/ */
public function getVenueTime($venue_id, $date) public function getVenueTime($date)
{ {
$venue_info = $this->model->where('id', '=', $venue_id)->find(); $timeSlots = [];
$venue_info['time_range'] = []; foreach ($date as $item) {
//可用时间范围 // 获取 time_range_type,默认为空
if ($venue_info['time_range_type'] === 'range') { $timeRangeType = $item['time_range_type'] ?? '';
$time_range_start = $venue_info['time_range_start']; switch ($timeRangeType) {
$time_range_end = $venue_info['time_range_end']; case 'fixed':
for ($i = $time_range_start; $i <= $time_range_end; $i++) { // 固定时间段
$venue_info['time_range'][] = $i; if (!empty($item['fixed_time_ranges'])) {
try {
$fixedRanges = json_decode($item['fixed_time_ranges'], true);
foreach ($fixedRanges as $range) {
if (isset($range['start_time'], $range['end_time'])) {
$timeSlots[] = "{$range['start_time']}-{$range['end_time']}";
}
}
} catch (\Exception $e) {
// 记录错误日志
\think\facade\Log::error('解析固定时间段失败: ' . $e->getMessage());
}
}
break;
case 'all':
// 全天可用,但中午12:30-14:00不可用
// 上午:8:00 - 12:30(每小时)
for ($hour = 8; $hour <= 12; $hour++) {
$startTime = sprintf('%02d:%02d', $hour, 0);
$endTime = sprintf('%02d:%02d', $hour + 1, 0);
if ("12:30" === "$startTime") continue;
$timeSlots[] = "$startTime-$endTime";
}
// 下午:14:00 - 21:00(每小时)
for ($hour = 14; $hour < 22; $hour++) {
$startTime = sprintf('%02d:%02d', $hour, 0);
$endTime = sprintf('%02d:%02d', $hour + 1, 0);
$timeSlots[] = "$startTime-$endTime";
}
break;
case 'range':
// 自定义时间范围
$start = $item['time_range_start'] ?? '';
$end = $item['time_range_end'] ?? '';
if ($start && $end) {
$startParts = explode(':', $start);
$endParts = explode(':', $end);
if (count($startParts) === 2 && count($endParts) === 2) {
$startTotalMinutes = intval($startParts[0]) * 60 + intval($startParts[1]);
$endTotalMinutes = intval($endParts[0]) * 60 + intval($endParts[1]);
if ($startTotalMinutes < $endTotalMinutes) {
for ($minutes = $startTotalMinutes; $minutes < $endTotalMinutes; $minutes += 60) {
$startHour = intdiv($minutes, 60);
$startMinute = $minutes % 60;
$endHour = intdiv($minutes + 60, 60);
$endMinute = ($minutes + 60) % 60;
$startTime = sprintf('%02d:%02d', $startHour, $startMinute);
$endTime = sprintf('%02d:%02d', $endHour, $endMinute);
$timeSlots[] = "$startTime-$endTime";
}
}
}
}
break;
default:
// 不支持的类型返回空数组
break;
} }
} }
// 固定使用时间 // $timeSlots去重
if ($venue_info['time_range_type'] === 'fixed') { return array_unique($timeSlots);
$venue_info['time_range'] = json_decode($venue_info['fixed_time_ranges'], true);
for ($i = 0; $i < count($venue_info['time_range']); $i++) {
$venue_info['time_range'][$i]['start'] = strtotime($date . ' ' . $venue_info['time_range'][$i]['start_time']);
$venue_info['time_range'][$i]['end'] = strtotime($date . ' ' . $venue_info['time_range'][$i]['end_time']);
}
}
// 全天可用从早上8点开始到晚上22点结束
if ($venue_info['time_range_type'] === 'all') {
for ($i = 8; $i <= 22; $i++) {
$venue_info['time_range'][] = $i;
}
}
return $venue_info;
} }
} }

156
niucloud/app/service/api/apiService/AttendanceService.php

@ -0,0 +1,156 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\api\apiService;
use app\model\attendance\Attendance;
use core\base\BaseApiService;
/**
* 考勤管理服务层
* Class MemberService
* @package app\service\api\member
*/
class AttendanceService extends BaseApiService
{
public function __construct()
{
parent::__construct();
$this->model = (new Attendance());
}
//列表
public function getList(array $where, string $field = '*')
{
$page_params = $this->getPageParam();//获取请求参数中的页码+分页数
$page = $page_params['page'];
$limit = $page_params['limit'];
$model = $this->model;
if (!empty($where['campus_id'])) {
$model = $model->where('campus_id', $where['campus_id']);
}
if (!empty($where['staff_id'])) {
$model = $model->where('staff_id', $where['staff_id']);
}
if (!empty($where['attendance_date'])) {
$model = $model->where('attendance_date', $where['attendance_date']);
}
if (!empty($where['status'])) {
$model = $model->where('status', $where['status']);
}
if (!empty($where['status_arr'])) {
$model = $model->whereIn('status', $where['status_arr']);
}
$res = $model->with([
'campus',
'personnel',
])->append([
'status_name'
])
->order('created_at','desc')
->paginate([
'list_rows' => $limit,
'page' => $page,
])->toArray();
return $res;
}
//查询详情
public function info(array $where, string $field = '*')
{
$res = [
'code' => 0,
'msg' => '操作失败',
'data' => [],
];
if (empty($where)) {
$res['msg'] = '筛选条件不能唯空';
return $res;
}
$model = $this->model;
if (!empty($where['id'])) {
$model = $model->where('id', $where['id']);
}
if (!empty($where['campus_id'])) {
$model = $model->where('campus_id', $where['campus_id']);
}
if (!empty($where['staff_id'])) {
$model = $model->where('staff_id', $where['staff_id']);
}
if (!empty($where['attendance_date'])) {
$model = $model->where('attendance_date', $where['attendance_date']);
}
$data = $model->field($field)->find();
if ($data) {
$res = [
'code' => 1,
'msg' => '操作成功',
'data' => $data->toArray(),
];
} else {
$res['msg'] = '暂无数据';
}
return $res;
}
//添加数据
public function addData(array $data)
{
$add = $this->model->create($data);
if ($add) {
$res = [
'code' => 1,
'msg' => '操作成功',
'data' => [],
];
} else {
$res = [
'code' => 0,
'msg' => '操作失败',
'data' => [],
];
}
return $res;
}
//编辑数据
public function editData(array $where, array $data)
{
$data['updated_at'] = date('Y-m-d H:i:s');
$model = $this->model;
if (!empty($where['id'])) {
$model = $model->where('id', $where['id']);
}
$edit = $model->update($data);
if ($edit) {
$res = [
'code' => 1,
'msg' => '操作成功',
'data' => [],
];
} else {
$res = [
'code' => 0,
'msg' => '操作失败',
'data' => [],
];
}
return $res;
}
}

32
niucloud/app/service/api/apiService/CommonService.php

@ -80,4 +80,36 @@ class CommonService extends BaseApiService
]; ];
} }
/**
* 判断是否在打卡范围内
* @param float $campusLat 地理围栏中心纬度
* @param float $campusLng 地理围栏中心经度
* @param float $checkLat 打卡纬度
* @param float $checkLng 打卡经度
* @param float $radius 地理围栏半径(单位:米)
* @return bool 返回是否在范围内的布尔值
*/
public function isInCheckRange(float $campusLat, float $campusLng, float $checkLat, float $checkLng, float $radius = 3000.0): bool
{
// 使用 Haversine 公式计算两点之间的距离
$earthRadius = 6371000; // 地球半径,单位为米
$latFrom = deg2rad($campusLat);
$lngFrom = deg2rad($campusLng);
$latTo = deg2rad($checkLat);
$lngTo = deg2rad($checkLng);
$latDelta = $latTo - $latFrom;
$lngDelta = $lngTo - $lngFrom;
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
cos($latFrom) * cos($latTo) * pow(sin($lngDelta / 2), 2)));
$distance = $angle * $earthRadius;
// 判断计算出的距离是否小于等于设定的围栏半径
return $distance <= $radius;//false=超出打卡范围
}
} }

4
niucloud/app/service/api/apiService/TeachingResearchService.php

@ -39,7 +39,7 @@ class TeachingResearchService extends BaseApiService
//获取教研管理文章列表 //获取教研管理文章列表
public function list($id,$table_type){ public function list($id,$table_type){
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,url'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,url,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$LessonCourseTeaching = new LessonCourseTeaching(); $LessonCourseTeaching = new LessonCourseTeaching();
$where = []; $where = [];
@ -56,7 +56,7 @@ class TeachingResearchService extends BaseApiService
//获取教研管理文章详情 //获取教研管理文章详情
public function info($id){ public function info($id){
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,url'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,url,exam_papers_id';
$LessonCourseTeaching = new LessonCourseTeaching(); $LessonCourseTeaching = new LessonCourseTeaching();
$info = $LessonCourseTeaching->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $LessonCourseTeaching->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray();
if (is_array($info) && isset($info['type']) && $info['type'] == 2 && !empty($info['url']) && is_string($info['url'])) { if (is_array($info) && isset($info['type']) && $info['type'] == 2 && !empty($info['url']) && is_string($info['url'])) {

6
niucloud/app/validate/course_schedule/CourseSchedule.php

@ -25,11 +25,7 @@ class CourseSchedule extends BaseValidate
'course_date' => 'require', 'course_date' => 'require',
'time_slot' => 'require', 'time_slot' => 'require',
'course_id' => 'require', 'course_id' => 'require',
'coach_id' => 'require', 'coach_id' => 'require'
'participants' => 'require',
'student_ids' => 'require',
'available_capacity' => 'require',
'status' => 'require',
]; ];
protected $message = [ protected $message = [

6
niucloud/public/.gitignore

@ -1,4 +1,8 @@
/.htaccess /.htaccess
upload upload
nginx.htaccess nginx.htaccess
.htaccess .htaccess
addon
admin
wap
web

1
niucloud/public/addon/README.md

@ -1 +0,0 @@
插件资源文件目录

BIN
niucloud/public/addon/shop/Union.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
niucloud/public/addon/shop/VIP.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

BIN
niucloud/public/addon/shop/apply/tiaojian.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

BIN
niucloud/public/addon/shop/attachment/active_cube_01.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

BIN
niucloud/public/addon/shop/attachment/active_cube_02.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

BIN
niucloud/public/addon/shop/attachment/active_cube_03.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

BIN
niucloud/public/addon/shop/attachment/active_cube_04.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save