Browse Source

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

master
李双庆 10 months ago
parent
commit
4d63d007b3
  1. 58
      admin/src/app/api/class_resources_rel.ts
  2. 9
      admin/src/app/api/course.ts
  3. 10
      admin/src/app/api/course_schedule.ts
  4. 4
      admin/src/app/api/customer_resources.ts
  5. 4
      admin/src/app/api/exam_papers.ts
  6. 4
      admin/src/app/api/exam_questions.ts
  7. 4
      admin/src/app/api/person_course_schedule.ts
  8. 19
      admin/src/app/api/sys.ts
  9. 17
      admin/src/app/lang/zh-cn/class_resources_rel.class_resources_rel.json
  10. 1
      admin/src/app/lang/zh-cn/course.course.json
  11. 42
      admin/src/app/lang/zh-cn/course_schedule.course_schedule.json
  12. 28
      admin/src/app/views/auth/components/edit-role.vue
  13. 7
      admin/src/app/views/auth/role.vue
  14. 20
      admin/src/app/views/campus_person_role/campus_person_role.vue
  15. 34
      admin/src/app/views/campus_person_role/components/campus-person-role-edit.vue
  16. 201
      admin/src/app/views/class_resources_rel/class_resources_rel.vue
  17. 251
      admin/src/app/views/class_resources_rel/components/class-resources-rel-edit.vue
  18. 28
      admin/src/app/views/communication_records/communication_records.vue
  19. 149
      admin/src/app/views/course/components/change_course.vue
  20. 12
      admin/src/app/views/course/course.vue
  21. 376
      admin/src/app/views/course_schedule/components/course-schedule-edit.vue
  22. 180
      admin/src/app/views/course_schedule/course_schedule.vue
  23. 752
      admin/src/app/views/customer_resources/components/customer-resources-edit.vue
  24. 3
      admin/src/app/views/exam_papers/components/exam-papers-edit.vue
  25. 5
      admin/src/app/views/exam_questions/components/exam-questions-edit.vue
  26. 58
      admin/src/app/views/exam_questions/exam_questions.vue
  27. 228
      admin/src/app/views/jlyj/jlyj.vue
  28. 11
      admin/src/app/views/login/index.vue
  29. 2
      admin/src/app/views/order_table/order_table.vue
  30. 12
      admin/src/app/views/service/components/service-edit.vue
  31. 188
      admin/src/app/views/statistics/home.vue
  32. 2
      admin/src/app/views/student/components/student-edit.vue
  33. 19
      admin/src/app/views/student/student.vue
  34. 272
      admin/src/app/views/timetables/components/schedule-add.vue
  35. 291
      admin/src/app/views/timetables/components/seat-selector.vue
  36. 541
      admin/src/app/views/timetables/timetables.vue
  37. 113
      admin/src/utils/timeslots.ts
  38. BIN
      niucloud/app.zip
  39. 8
      niucloud/app/adminapi/controller/campus_person_role/CampusPersonRole.php
  40. 104
      niucloud/app/adminapi/controller/class_resources_rel/ClassResourcesRel.php
  41. 4
      niucloud/app/adminapi/controller/communication_records/CommunicationRecords.php
  42. 91
      niucloud/app/adminapi/controller/course/Course.php
  43. 83
      niucloud/app/adminapi/controller/course_schedule/CourseSchedule.php
  44. 184
      niucloud/app/adminapi/controller/customer_resources/CustomerResources.php
  45. 5
      niucloud/app/adminapi/controller/exam_papers/ExamPapers.php
  46. 6
      niucloud/app/adminapi/controller/exam_questions/ExamQuestions.php
  47. 52
      niucloud/app/adminapi/controller/lesson_course_teaching/LessonCourseTeaching.php
  48. 1
      niucloud/app/adminapi/controller/order_table/OrderTable.php
  49. 21
      niucloud/app/adminapi/controller/person_course_schedule/PersonCourseSchedule.php
  50. 2
      niucloud/app/adminapi/controller/sys/Role.php
  51. 28
      niucloud/app/adminapi/controller/sys/System.php
  52. 43
      niucloud/app/adminapi/route/class_resources_rel.php
  53. 2
      niucloud/app/adminapi/route/classroom.php
  54. 5
      niucloud/app/adminapi/route/course.php
  55. 2
      niucloud/app/adminapi/route/course_schedule.php
  56. 5
      niucloud/app/adminapi/route/customer_resources.php
  57. 1
      niucloud/app/adminapi/route/exam_papers.php
  58. 1
      niucloud/app/adminapi/route/exam_questions.php
  59. 3
      niucloud/app/adminapi/route/person_course_schedule.php
  60. 5
      niucloud/app/adminapi/route/sys.php
  61. 189
      niucloud/app/api/controller/apiController/Chat.php
  62. 12
      niucloud/app/api/route/route.php
  63. 7
      niucloud/app/common.php
  64. 91
      niucloud/app/model/chat_friends/ChatFriends.php
  65. 50
      niucloud/app/model/chat_messages/ChatMessages.php
  66. 68
      niucloud/app/model/class_resources_rel/ClassResourcesRel.php
  67. 50
      niucloud/app/model/course_schedule/CourseSchedule.php
  68. 5
      niucloud/app/model/customer_resources/CustomerResources.php
  69. 31
      niucloud/app/model/person_course_schedule/PersonCourseSchedule.php
  70. 2
      niucloud/app/model/venue/Venue.php
  71. 36
      niucloud/app/service/admin/campus_person_role/CampusPersonRoleService.php
  72. 117
      niucloud/app/service/admin/class_resources_rel/ClassResourcesRelService.php
  73. 31
      niucloud/app/service/admin/communication_records/CommunicationRecordsService.php
  74. 21
      niucloud/app/service/admin/course/CourseService.php
  75. 184
      niucloud/app/service/admin/course_schedule/CourseScheduleService.php
  76. 159
      niucloud/app/service/admin/customer_resources/CustomerResourcesService.php
  77. 11
      niucloud/app/service/admin/exam_papers/ExamPapersService.php
  78. 79
      niucloud/app/service/admin/exam_questions/ExamQuestionsService.php
  79. 28
      niucloud/app/service/admin/lesson_course_teaching/LessonCourseTeachingService.php
  80. 108
      niucloud/app/service/admin/person_course_schedule/PersonCourseScheduleService.php
  81. 14
      niucloud/app/service/admin/student/StudentService.php
  82. 14
      niucloud/app/service/admin/sys/RoleService.php
  83. 77
      niucloud/app/service/admin/sys/SystemService.php
  84. 89
      niucloud/app/service/admin/venue/VenueService.php
  85. 216
      niucloud/app/service/api/apiService/ChatService.php
  86. 23
      niucloud/app/service/api/pay/PayService.php
  87. 35
      niucloud/app/validate/class_resources_rel/ClassResourcesRel.php
  88. 6
      niucloud/app/validate/course_schedule/CourseSchedule.php
  89. 18
      niucloud/app/validate/person_course_schedule/PersonCourseSchedule.php

58
admin/src/app/api/class_resources_rel.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- class_resources_rel
/**
*
* @param params
* @returns
*/
export function getClassResourcesRelList(params: Record<string, any>) {
return request.get(`class_resources_rel/class_resources_rel`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getClassResourcesRelInfo(id: number) {
return request.get(`class_resources_rel/class_resources_rel/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addClassResourcesRel(params: Record<string, any>) {
return request.post('class_resources_rel/class_resources_rel', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editClassResourcesRel(params: Record<string, any>) {
return request.put(`class_resources_rel/class_resources_rel/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteClassResourcesRel(id: number) {
return request.delete(`class_resources_rel/class_resources_rel/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithClassGradeList(params: Record<string,any>){
return request.get('class_resources_rel/class_grade_all', {params})
}export function getWithCustomerResourcesList(params: Record<string,any>){
return request.get('class_resources_rel/customer_resources_all', {params})
}export function getWithCampusList(params: Record<string,any>){
return request.get('class_resources_rel/campus_all', {params})
}
// USER_CODE_END -- class_resources_rel

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

@ -19,6 +19,12 @@ export function getCourseInfo(id: number) {
return request.get(`course/course/${id}`) return request.get(`course/course/${id}`)
} }
export function contractAll(params: Record<string, any>) {
return request.get(`course/contract_all`, { params })
}
/** /**
* *
* @param params * @param params
@ -56,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

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

@ -62,7 +62,15 @@ 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 })
} }
/**通过课程id获取人员列表 */
export function getCourseStudents(id: number) {
return request.get(`customer_resources/getResourceByCourse/${id}/students`)
}
/**通过名字或手机号获取人员列表 */
export function getResourceByNameOrPhone(params: Record<string, any>) {
return request.get(`customer_resources/personnel_all_byname`, { 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 })
}

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

@ -50,5 +50,9 @@ export function deleteExamPapers(id: number) {
} }
export function getAll() {
return request.get(`exam_papers/all`)
}
// USER_CODE_END -- exam_papers // USER_CODE_END -- exam_papers

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

@ -55,3 +55,7 @@ export function randomQuestionsList(params: Record<string, any>) {
// USER_CODE_END -- exam_questions // USER_CODE_END -- exam_questions
export function toLeadInto(params: Record<string, any>) {
return request.post(`exam_questions/to_lead_into`, params, { showErrorMessage: true, showSuccessMessage: true })
}

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

@ -58,5 +58,9 @@ export function deletePersonCourseSchedule(id: number) {
showSuccessMessage: true, showSuccessMessage: true,
}) })
} }
/**获取试课人员列表 */
export function getTryCoursePerson(id: number) {
return request.get(`person_course_schedule/get_try_course_person/${id}`)
}
// USER_CODE_END -- person_course_schedule // USER_CODE_END -- person_course_schedule

19
admin/src/app/api/sys.ts

@ -772,3 +772,22 @@ export function xsyjConfig(params: Record<string, any>) {
export function getXsyjConfig() { export function getXsyjConfig() {
return request.get('sys/get_xsyj_config') return request.get('sys/get_xsyj_config')
} }
export function jlyjConfig(params: Record<string, any>) {
return request.post(`sys/jlyj_config`, params)
}
export function getJlyjConfig() {
return request.get('sys/get_jlyj_config')
}
export function getHome(params: Record<string, any>) {
return request.post('sys/home', params)
}
export function setDocument(params: Record<string, any>) {
return request.post('sys/document/document', params)
}

17
admin/src/app/lang/zh-cn/class_resources_rel.class_resources_rel.json

@ -0,0 +1,17 @@
{
"classId":"班级",
"classIdPlaceholder":"请输入班级",
"resourceId":"资源",
"resourceIdPlaceholder":"请输入资源",
"campusId":"校区",
"campusIdPlaceholder":"请输入校区",
"sourceType":"数据资源类型",
"sourceTypePlaceholder":"请输入数据资源类型",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addClassResourcesRel":"添加班级和资源",
"updateClassResourcesRel":"编辑班级和资源",
"classResourcesRelDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

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

@ -24,4 +24,5 @@
"courseDeleteTips": "确定要删除该数据吗?", "courseDeleteTips": "确定要删除该数据吗?",
"startDate": "请选择开始时间", "startDate": "请选择开始时间",
"endDate": "请选择结束时间" "endDate": "请选择结束时间"
} }

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": "否"
} }

28
admin/src/app/views/auth/components/edit-role.vue

@ -42,6 +42,25 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="部门" >
<el-select
class="input-width"
v-model="formData.dept_id"
clearable
placeholder="请选择部门"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in parentDepartmentIdList"
:key="index"
:label="item['department_name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('permission')" prop="rules"> <el-form-item :label="t('permission')" prop="rules">
<div class="flex items-center justify-between w-11/12"> <div class="flex items-center justify-between w-11/12">
<div> <div>
@ -86,6 +105,8 @@
<script lang="ts" setup async> <script lang="ts" setup async>
import {ref, reactive, computed, watch, toRaw} from 'vue' import {ref, reactive, computed, watch, toRaw} from 'vue'
import {t} from '@/lang' import {t} from '@/lang'
import {
getWithDepartmentsList} from '@/app/api/departments'
import type {FormInstance} from 'element-plus' import type {FormInstance} from 'element-plus'
import {addRole, editRole, getRoleInfo} from '@/app/api/sys' import {addRole, editRole, getRoleInfo} from '@/app/api/sys'
import {getAuthMenus} from '@/app/api/auth' import {getAuthMenus} from '@/app/api/auth'
@ -116,6 +137,12 @@ watch(selectAll, () => {
} }
}) })
const parentDepartmentIdList = ref([] as any[])
const setParentDepartmentIdList = async () => {
parentDepartmentIdList.value = await (await getWithDepartmentsList({})).data
}
setParentDepartmentIdList()
const roleKeyList = ref([]) const roleKeyList = ref([])
const getRolekeyDictList = async () => { const getRolekeyDictList = async () => {
roleKeyList.value = await ( roleKeyList.value = await (
@ -162,6 +189,7 @@ const initialFormData = {
role_name: '', role_name: '',
status: 1, status: 1,
role_key: '', role_key: '',
dept_id:'',
rules: [], rules: [],
} }
const formData: Record<string, any> = reactive({...initialFormData}) const formData: Record<string, any> = reactive({...initialFormData})

7
admin/src/app/views/auth/role.vue

@ -71,6 +71,13 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
prop="department_name"
label="部门"
></el-table-column>
<el-table-column <el-table-column
prop="create_time" prop="create_time"
:label="t('createTime')" :label="t('createTime')"

20
admin/src/app/views/campus_person_role/campus_person_role.vue

@ -34,7 +34,13 @@
</el-form-item> </el-form-item>
<el-form-item :label="t('personId')" prop="person_id"> <el-form-item :label="t('personId')" prop="person_id">
<el-select
<el-input
v-model="campusPersonRoleTable.searchParam.person_name"
:placeholder="t('personIdPlaceholder')"
/>
<!-- <el-select
class="w-[280px]" class="w-[280px]"
v-model="campusPersonRoleTable.searchParam.person_id" v-model="campusPersonRoleTable.searchParam.person_id"
clearable clearable
@ -46,7 +52,7 @@
:label="item['name']" :label="item['name']"
:value="item['id']" :value="item['id']"
/> />
</el-select> </el-select> -->
</el-form-item> </el-form-item>
<!-- <el-form-item :label="t('roleId')" prop="role_id"> <!-- <el-form-item :label="t('roleId')" prop="role_id">
@ -117,6 +123,14 @@
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> />
<el-table-column
prop="phone"
label="电话"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column <el-table-column
prop="role_id_name" prop="role_id_name"
:label="t('roleId')" :label="t('roleId')"
@ -194,7 +208,7 @@ let campusPersonRoleTable = reactive({
data: [], data: [],
searchParam: { searchParam: {
campus_id: '', campus_id: '',
person_id: '', person_name: '',
role_id: '', role_id: '',
dept_id: '', dept_id: '',
}, },

34
admin/src/app/views/campus_person_role/components/campus-person-role-edit.vue

@ -50,6 +50,18 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- <el-form-item :label="电话">
<el-input
v-model="formData.age_group"
type="number"
clearable
:placeholder="t('ageGroupPlaceholder')"
class="input-width"
/>
</el-form-item> -->
<el-form-item :label="t('roleId')" prop="role_id"> <el-form-item :label="t('roleId')" prop="role_id">
<el-select <el-select
class="input-width" class="input-width"
@ -67,7 +79,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('deptId')" v-if="!formData.dept_id"> <!-- <el-form-item :label="t('deptId')" v-if="!formData.dept_id">
<el-select <el-select
class="input-width" class="input-width"
v-model="formData.dept_id" v-model="formData.dept_id"
@ -81,8 +93,8 @@
:label="item['department_name']" :label="item['department_name']"
:value="item['id']" :value="item['id']"
/> />
</el-select> </el-select> -->
</el-form-item> <!-- </el-form-item> -->
</el-form> </el-form>
<template #footer> <template #footer>
@ -104,6 +116,8 @@ import { ref, reactive, computed, watch } 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'
import { useRoute } from 'vue-router'
const route = useRoute()
import { import {
addCampusPersonRole, addCampusPersonRole,
editCampusPersonRole, editCampusPersonRole,
@ -116,6 +130,8 @@ import {
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
const pageName = route.meta.title
/** /**
* 表单数据 * 表单数据
@ -125,8 +141,16 @@ const initialFormData = {
campus_id: '', campus_id: '',
person_id: '', person_id: '',
role_id: '', role_id: '',
dept_id: '', dept_id: ''
} }
if(pageName == '市场人员列表'){
initialFormData.dept_id = 1;
}else if(pageName == '销售人员列表'){
initialFormData.dept_id = 2;
// campusPersonRoleTable.searchParam.role_id = 2;
}
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
@ -192,7 +216,7 @@ const setPersonIdList = async () => {
setPersonIdList() setPersonIdList()
const roleIdList = ref([] as any[]) const roleIdList = ref([] as any[])
const setRoleIdList = async () => { const setRoleIdList = async () => {
roleIdList.value = await (await getWithSysRoleList({})).data roleIdList.value = await (await getWithSysRoleList({dept_id:formData.dept_id})).data
} }
setRoleIdList() setRoleIdList()
const deptIdList = ref([] as any[]) const deptIdList = ref([] as any[])

201
admin/src/app/views/class_resources_rel/class_resources_rel.vue

@ -0,0 +1,201 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-lg">{{pageName}}</span>
<el-button type="primary" @click="addEvent">
{{ t('addClassResourcesRel') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="classResourcesRelTable.searchParam" ref="searchFormRef">
<el-form-item>
<el-button type="primary" @click="loadClassResourcesRelList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table :data="classResourcesRelTable.data" size="large" v-loading="classResourcesRelTable.loading">
<template #empty>
<span>{{ !classResourcesRelTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="class_id_name" :label="t('classId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="resource_id_name" :label="t('resourceId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="campus_id_name" :label="t('campusId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('sourceType')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in source_typeList">
<div v-if="item.value == row.source_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)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="classResourcesRelTable.page" v-model:page-size="classResourcesRelTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="classResourcesRelTable.total"
@size-change="loadClassResourcesRelList()" @current-change="loadClassResourcesRelList" />
</div>
</div>
<edit ref="editClassResourcesRelDialog" @complete="loadClassResourcesRelList" />
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import { getClassResourcesRelList, deleteClassResourcesRel, getWithClassGradeList, getWithCustomerResourcesList, getWithCampusList } from '@/app/api/class_resources_rel'
import { img } from '@/utils/common'
import { ElMessageBox,FormInstance } from 'element-plus'
import Edit from '@/app/views/class_resources_rel/components/class-resources-rel-edit.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title;
let classResourcesRelTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const source_typeList = ref([] as any[])
const source_typeDictList = async () => {
source_typeList.value = await (await useDictionary('sj_type')).data.dictionary
}
source_typeDictList();
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('student_status')).data.dictionary
}
statusDictList();
/**
* 获取班级和资源列表
*/
const loadClassResourcesRelList = (page: number = 1) => {
classResourcesRelTable.loading = true
classResourcesRelTable.page = page
getClassResourcesRelList({
page: classResourcesRelTable.page,
limit: classResourcesRelTable.limit,
...classResourcesRelTable.searchParam
}).then(res => {
classResourcesRelTable.loading = false
classResourcesRelTable.data = res.data.data
classResourcesRelTable.total = res.data.total
}).catch(() => {
classResourcesRelTable.loading = false
})
}
loadClassResourcesRelList()
const editClassResourcesRelDialog: Record<string, any> | null = ref(null)
/**
* 添加班级和资源
*/
const addEvent = () => {
editClassResourcesRelDialog.value.setFormData()
editClassResourcesRelDialog.value.showDialog = true
}
/**
* 编辑班级和资源
* @param data
*/
const editEvent = (data: any) => {
editClassResourcesRelDialog.value.setFormData(data)
editClassResourcesRelDialog.value.showDialog = true
}
/**
* 删除班级和资源
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('classResourcesRelDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteClassResourcesRel(id).then(() => {
loadClassResourcesRelList()
}).catch(() => {
})
})
}
const classIdList = ref([])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassGradeList({})).data
}
setClassIdList()
const resourceIdList = ref([])
const setResourceIdList = async () => {
resourceIdList.value = await (await getWithCustomerResourcesList({})).data
}
setResourceIdList()
const campusIdList = ref([])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadClassResourcesRelList()
}
</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>

251
admin/src/app/views/class_resources_rel/components/class-resources-rel-edit.vue

@ -0,0 +1,251 @@
<template>
<el-dialog v-model="showDialog" title="班级分配" 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('campusId')" >
<el-select class="input-width" v-model="formData.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in campusIdList"
:key="index"
:label="item['campus_name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('classId')" prop="class_id">
<el-select class="input-width" v-model="formData.class_id" clearable :placeholder="t('classIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in classIdList"
:key="index"
:label="item['class_name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<!-- <el-form-item label="资源" >
<el-select class="input-width" v-model="formData.resource_id" clearable :placeholder="t('resourceIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in resourceIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item> -->
<el-form-item label="数据资源类型" >
<el-select class="input-width" v-model="formData.source_type" clearable placeholder="请选择数据资源类型">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in source_typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('status')" >
<el-select class="input-width" v-model="formData.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="Number(item.value)"
/>
</el-select>
</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 { addClassResourcesRel, editClassResourcesRel, getClassResourcesRelInfo, getWithClassGradeList, getWithCustomerResourcesList, getWithCampusList } from '@/app/api/class_resources_rel'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
class_id: '',
// resource_id: '',
campus_id: '',
source_type: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
class_id: [
{ required: true, message: t('classIdPlaceholder'), trigger: 'blur' },
]
,
resource_id: [
{ required: true, message: t('resourceIdPlaceholder'), trigger: 'blur' },
]
,
campus_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
]
,
source_type: [
{ required: true, message: t('sourceTypePlaceholder'), 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 ? editClassResourcesRel : addClassResourcesRel
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 source_typeList = ref([])
const source_typeDictList = async () => {
source_typeList.value = await (await useDictionary('sj_type')).data.dictionary
}
source_typeDictList();
watch(() => source_typeList.value, () => { formData.source_type = source_typeList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('student_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const classIdList = ref([] as any[])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassGradeList({})).data
}
setClassIdList()
const resourceIdList = ref([] as any[])
const setResourceIdList = async () => {
resourceIdList.value = await (await getWithCustomerResourcesList({})).data
}
setResourceIdList()
const campusIdList = ref([] as any[])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if(row){
const data = await (await getClassResourcesRelInfo(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>

28
admin/src/app/views/communication_records/communication_records.vue

@ -3,9 +3,9 @@
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class="text-lg">{{ pageName }}</span> <span class="text-lg">{{ pageName }}</span>
<el-button type="primary" @click="addEvent"> <!-- <el-button type="primary" @click="addEvent">
{{ t('addCommunicationRecords') }} {{ t('addCommunicationRecords') }}
</el-button> </el-button> -->
</div> </div>
<el-card <el-card
@ -17,7 +17,19 @@
:model="communicationRecordsTable.searchParam" :model="communicationRecordsTable.searchParam"
ref="searchFormRef" ref="searchFormRef"
> >
<el-form-item :label="t('resourceId')" prop="resource_id">
<el-form-item label="名称" prop="name">
<el-input v-model="communicationRecordsTable.searchParam.name" />
</el-form-item>
<el-form-item label="电话" prop="phone_number">
<el-input v-model="communicationRecordsTable.searchParam.phone_number" />
</el-form-item>
<el-form-item label="校区" prop="campus">
<el-input v-model="communicationRecordsTable.searchParam.campus" />
</el-form-item>
<!-- <el-form-item :label="t('resourceId')" prop="resource_id">
<el-select <el-select
class="w-[280px]" class="w-[280px]"
v-model="communicationRecordsTable.searchParam.resource_id" v-model="communicationRecordsTable.searchParam.resource_id"
@ -31,7 +43,7 @@
:value="item['id']" :value="item['id']"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item> -->
<el-form-item <el-form-item
:label="t('communicationType')" :label="t('communicationType')"
@ -155,9 +167,9 @@
<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)">{{ <!-- <el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete') t('delete')
}}</el-button> }}</el-button> -->
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -204,7 +216,9 @@ let communicationRecordsTable = reactive({
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam: {
resource_id: '', name: '',
phone_number: '',
campus: '',
communication_type: '', communication_type: '',
}, },
}) })

149
admin/src/app/views/course/components/change_course.vue

@ -0,0 +1,149 @@
<template>
<el-dialog
v-model="showDialog"
:title="formData.id ? t('updateCourse') : t('addCourse')"
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="合同" prop="contract_id">
<el-select
class="input-width"
v-model="formData.contract_id"
placeholder="请选择合同"
>
<el-option
v-for="(item, index) in contractList"
:key="index"
:label="item.contract_name"
:value="item.id"
/>
</el-select>
</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 { addCourse, editCourse, getCourseInfo,contractAll } from '@/app/api/course'
let showDialog = ref(false)
const loading = ref(false)
const contractList : any = ref(null)
//
const getContractAll = async () => {
contractList.value = await (await contractAll()).data
}
getContractAll()
/**
* 表单数据
*/
const initialFormData = {
id: '',
course_name: '',
course_type: '',
duration: '',
session_count: '',
single_session_count: '',
price: '',
internal_reminder: '',
customer_reminder: '',
remarks: '',
contract_id: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editCourse : addCourse
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
})
}
})
}
//
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getCourseInfo(row.id)).data
if (data)
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

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

@ -133,6 +133,8 @@
<el-button type="primary" link @click="deleteEvent(row.id)">{{ <el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete') t('delete')
}}</el-button> }}</el-button>
<el-button type="primary" link @click="contractEvent(row)">关联合同</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -149,6 +151,7 @@
</div> </div>
<edit ref="editCourseDialog" @complete="loadCourseList" /> <edit ref="editCourseDialog" @complete="loadCourseList" />
<contract ref="contractDialog" @complete="loadCourseList" />
</el-card> </el-card>
</div> </div>
</template> </template>
@ -161,6 +164,7 @@ import { getCourseList, deleteCourse } from '@/app/api/course'
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/components/course-edit.vue' import Edit from '@/app/views/course/components/course-edit.vue'
import Contract from '@/app/views/course/components/change_course.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
@ -243,6 +247,14 @@ const editEvent = (data: any) => {
editCourseDialog.value.showDialog = true editCourseDialog.value.showDialog = true
} }
const contractDialog: Record<string, any> | null = ref(null)
const contractEvent = (data: any) => {
contractDialog.value.setFormData(data)
contractDialog.value.showDialog = true
}
/** /**
* 删除课程 * 删除课程
*/ */

376
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-option
v-for="item in coachList"
:key="item.id"
:label="item.name"
:value="item.id"
/> />
</el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('participants')" prop="participants"> <el-form-item :label="t('autoSchedule')" prop="auto_schedule">
<el-input <el-radio-group v-model="formData.auto_schedule">
v-model="formData.participants" <el-radio :label="1">{{ t('yes') }}</el-radio>
clearable <el-radio :label="0">{{ t('no') }}</el-radio>
:placeholder="t('participantsPlaceholder')" </el-radio-group>
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 :label="t('status')" prop="status">
<el-input
v-model="formData.status"
clearable
:placeholder="t('statusPlaceholder')"
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
isInitializing.value = true
try {
// 1. ()
let detailData = null;
if (row) { if (row) {
const data = await (await getCourseScheduleInfo(row.id)).data const response = await getCourseScheduleInfo(row.id);
if (data) detailData = response.data;
Object.keys(formData).forEach((key: string) => { }
if (data[key] != undefined) formData[key] = data[key]
// 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;

180
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)

752
admin/src/app/views/customer_resources/components/customer-resources-edit.vue

File diff suppressed because it is too large

3
admin/src/app/views/exam_papers/components/exam-papers-edit.vue

@ -80,6 +80,7 @@
<template #default="{ row }"> <template #default="{ row }">
<el-avatar v-if="row.question_content_type == 'image'" <el-avatar v-if="row.question_content_type == 'image'"
:src="img(row.question_content)" /> :src="img(row.question_content)" />
<div v-if="row.question_content_type == 'text'">{{ row.question_content }}</div> <div v-if="row.question_content_type == 'text'">{{ row.question_content }}</div>
</template> </template>
</el-table-column> </el-table-column>
@ -137,7 +138,7 @@
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { addExamPapers, editExamPapers, getExamPapersInfo } from '@/app/api/exam_papers' import { addExamPapers, editExamPapers, getExamPapersInfo } from '@/app/api/exam_papers'
import { getExamQuestionsList,randomQuestionsList } from '@/app/api/exam_questions' import { getExamQuestionsList,randomQuestionsList } from '@/app/api/exam_questions'
import { img } from '@/utils/common'
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)

5
admin/src/app/views/exam_questions/components/exam-questions-edit.vue

@ -83,7 +83,7 @@
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="addOption">添加选项</el-button> <el-button type="primary" @click="addOption" v-if="formData.option_json.length <= 3">添加选项</el-button>
</el-form-item> </el-form-item>
@ -132,14 +132,15 @@
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const addOption = () => { const addOption = () => {
const index = formData.option_json.length; const index = formData.option_json.length;
formData.option_json.push({ formData.option_json.push({
option_content_type: 'text', option_content_type: 'text',
option_content: '', option_content: '',
correct_answer: false, correct_answer: false,
option:alphabet[index] || '' option:alphabet[index] || ''
}) })
} }
const removeOption = (index) => { const removeOption = (index) => {

58
admin/src/app/views/exam_questions/exam_questions.vue

@ -35,6 +35,13 @@
<el-form-item> <el-form-item>
<el-button type="primary" @click="loadExamQuestionsList()">{{ t('search') }}</el-button> <el-button type="primary" @click="loadExamQuestionsList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button> <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
<el-button @click="DownloadExcel">下载导入模板</el-button>
<el-upload v-bind="upload" style="margin-left: 10px;">
<el-button type="primary">导入</el-button>
</el-upload>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@ -98,11 +105,13 @@
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { getExamQuestionsList, deleteExamQuestions } from '@/app/api/exam_questions' import { getExamQuestionsList, deleteExamQuestions,toLeadInto } from '@/app/api/exam_questions'
import { img } from '@/utils/common' import { img,getToken } from '@/utils/common'
import { ElMessageBox,FormInstance } from 'element-plus' import { ElMessageBox,FormInstance } from 'element-plus'
import Edit from '@/app/views/exam_questions/components/exam-questions-edit.vue' import Edit from '@/app/views/exam_questions/components/exam-questions-edit.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { UploadFile, ElMessage } from 'element-plus'
const route = useRoute() const route = useRoute()
const pageName = route.meta.title; const pageName = route.meta.title;
@ -121,6 +130,51 @@ let examQuestionsTable = reactive({
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
const DownloadExcel = () => {
const link = document.createElement('a');
link.href = 'https://zh.hnhbty.cn/excel.xlsx'; //
link.download = '题目导入模板.xlsx';
link.click();
}
const prop = defineProps({
modelValue: {
type: String,
default: '',
},
api: {
type: String,
default: 'sys/document/document',
},
})
const upload: Record<string, any> = {
action: `${import.meta.env.VITE_APP_BASE_URL}/${prop.api}`,
showFileList: false,
headers: {},
accept: '.xls,.xlsx',
onSuccess: (response: any, uploadFile: UploadFile) => {
if (response.code != undefined && response.code != 1) {
ElMessage({ message: response.msg, type: 'error' })
return
}
Import(response.data.url);
},
}
upload.headers[import.meta.env.VITE_REQUEST_HEADER_TOKEN_KEY] = getToken()
const Import = async(url) => {
examQuestionsTable.loading = true
toLeadInto({url:url}).then(res => {
examQuestionsTable.loading = false
loadExamQuestionsList();
}).catch(() => {
examQuestionsTable.loading = false
})
}
// //
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])

228
admin/src/app/views/jlyj/jlyj.vue

@ -0,0 +1,228 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never" v-loading="loading">
<div class="flex justify-between items-center">
<span class="text-lg">{{ pageName }}</span>
<el-button type="primary" @click="addStage"> 新增阶段 </el-button>
</div>
</el-card>
<el-card class="box-card !border-none" shadow="never">
<div
class="flex items-center justify-between p-[10px] table-item-border bg"
>
<span class="text-base w-[230px]">阶段名称</span>
<span class="text-base w-[110px] text-center">底薪</span>
</div>
<el-collapse v-model="activeNames" accordion>
<el-collapse-item
v-for="(stage, index) in stages"
:key="stage.id"
:name="stage.id"
>
<template #title>
<div class="collapse-title">
<span class="title-name">{{ stage.name }}</span>
<span class="arrow">{{ stage.price }} </span>
<!-- <span class="arrow">&gt;</span> -->
</div>
</template>
<el-form label-width="100px" style="margin-bottom: 10px">
<el-form-item label="阶段名称">
<el-input v-model="stage.name" placeholder="请输入阶段名称" />
</el-form-item>
</el-form>
<el-form label-width="100px" style="margin-bottom: 10px">
<el-form-item label="阶段底薪">
<el-input v-model="stage.price" placeholder="请输入阶段底薪" />
</el-form-item>
</el-form>
<el-button type="success" size="small" @click="addRule(stage)"
>新增规则</el-button
>
<el-table :data="stage.rules" border style="margin-top: 10px">
<el-table-column prop="renewal_standard_min" label="续费上限">
<template #default="{ row }">
<el-input
v-model="row.renewal_standard_min"
placeholder="请输入续费上限"
/>
</template>
</el-table-column>
<el-table-column prop="renewal_standard_max" label="续费下限">
<template #default="{ row }">
<el-input
v-model="row.renewal_standard_max"
placeholder="请输入续费下限"
/>
</template>
</el-table-column>
<el-table-column prop="renewal_commission" label="续费提成">
<template #default="{ row }">
<el-input v-model="row.renewal_commission" placeholder="%" />
</template>
</el-table-column>
<el-table-column prop="new_count_min" label="新单成交数上限">
<template #default="{ row }">
<el-input v-model="row.new_count_min" />
</template>
</el-table-column>
<el-table-column prop="new_count_max" label="新单成交数下限">
<template #default="{ row }">
<el-input v-model="row.new_count_max" />
</template>
</el-table-column>
<el-table-column prop="new_move_5" label="新招(5+1)x3">
<template #default="{ row }">
<el-input v-model="row.new_move_5" />
</template>
</el-table-column>
<el-table-column prop="new_move_7" label="新招(7+1)x3">
<template #default="{ row }">
<el-input v-model="row.new_move_7" />
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ $index }">
<el-button
type="danger"
size="small"
@click="removeRule(stage, $index)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<el-button
type="danger"
size="small"
style="margin-top: 10px"
@click="removeStage(index)"
>删除该阶段</el-button
>
</el-collapse-item>
</el-collapse>
<div style="text-align: right; margin-top: 20px">
<el-button type="primary" @click="onSave">提交保存</el-button>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { jlyjConfig, getJlyjConfig } from '@/app/api/sys'
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
const loading = ref(true)
const stages = ref([])
const activeNames = ref(null)
function addStage() {
const newStage = {
name: '默认阶段',
price: 0,
rules: [
{
renewal_standard_min: '',
renewal_standard_max: '',
renewal_commission: '',
new_count_min: '',
new_count_max: '',
new_move_5: '',
new_move_7: '',
},
],
}
stages.value.push(newStage)
activeNames.value = newStage.name
}
function removeStage(index) {
stages.value.splice(index, 1)
}
function addRule(stage) {
stage.rules.push({
renewal_standard_min: '',
renewal_standard_max: '',
renewal_commission: '',
new_count_min: '',
new_count_max: '',
new_move_5: '',
new_move_7: '',
})
}
function removeRule(stage, ruleIndex) {
if (stage.rules.length === 1) {
ElMessage.warning('至少保留一条规则')
return
}
stage.rules.splice(ruleIndex, 1)
}
const setFormData = async () => {
const data = await (await getJlyjConfig()).data
stages.value = data
loading.value = false
}
setFormData()
const onSave = async () => {
jlyjConfig(stages.value)
.then(() => {
loading.value = true
setFormData()
})
.catch(() => {
loading.value = false
})
}
</script>
<style lang="scss" scoped>
.collapse-title {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
font-size: 14px;
font-weight: 500;
padding-right: 10px;
color: #333;
padding: 10px;
}
.title-name {
width: 230px;
}
.title-salary {
width: 110px;
text-align: center;
color: #7438d5;
}
.arrow {
margin-left: auto;
color: #999;
font-size: 14px;
}
</style>

11
admin/src/app/views/login/index.vue

@ -187,6 +187,14 @@ const handleLogin = async (formEl: FormInstance | undefined) => {
}) })
} }
// const {
// query: { redirect },
// } = route
// const path = typeof redirect === 'string' ? redirect : '/'
// const url = router.resolve(path)
// console.log(url);
// console.log(route);
// data // data
const loginFn = (data = {}) => { const loginFn = (data = {}) => {
loading.value = true loading.value = true
@ -198,7 +206,8 @@ const loginFn = (data = {}) => {
} = route } = route
const path = typeof redirect === 'string' ? redirect : '/' const path = typeof redirect === 'string' ? redirect : '/'
const url = router.resolve(path) const url = router.resolve(path)
location.href = url.href // console.log(url);
location.href = '/'
}) })
.catch(() => { .catch(() => {
loading.value = false loading.value = false

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

@ -83,7 +83,7 @@
<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="startPayment(row.id)" v-if="row.order_status == 'pending'">支付</el-button> <el-button type="primary" link @click="startPayment(row.id)" v-if="row.order_status == 'pending' && row.payment_type == 'scan_code'">支付</el-button>
<el-button type="primary" link @click="editEvent(row)">{{ <el-button type="primary" link @click="editEvent(row)">{{
t('edit') t('edit')

12
admin/src/app/views/service/components/service-edit.vue

@ -28,7 +28,7 @@
v-for="(item, index) in staff_reminderList" v-for="(item, index) in staff_reminderList"
:key="index" :key="index"
:label="item.name" :label="item.name"
:value="item.value" :value="Number(item.value)"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -40,7 +40,7 @@
v-for="(item, index) in customer_reminderList" v-for="(item, index) in customer_reminderList"
:key="index" :key="index"
:label="item.name" :label="item.name"
:value="item.value" :value="Number(item.value)"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -52,14 +52,14 @@
v-for="(item, index) in customer_confirmationList" v-for="(item, index) in customer_confirmationList"
:key="index" :key="index"
:label="item.name" :label="item.name"
:value="item.value" :value="Number(item.value)"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('customerFeedback')" > <!-- <el-form-item :label="t('customerFeedback')" >
<el-input v-model="formData.customer_feedback" clearable :placeholder="t('customerFeedbackPlaceholder')" class="input-width" /> <el-input v-model="formData.customer_feedback" clearable :placeholder="t('customerFeedbackPlaceholder')" class="input-width" />
</el-form-item> </el-form-item> -->
<el-form-item :label="t('status')" prop="status"> <el-form-item :label="t('status')" prop="status">
<el-select class="input-width" v-model="formData.status" clearable :placeholder="t('statusPlaceholder')"> <el-select class="input-width" v-model="formData.status" clearable :placeholder="t('statusPlaceholder')">
@ -109,7 +109,7 @@ const initialFormData = {
staff_reminder: '', staff_reminder: '',
customer_reminder: '', customer_reminder: '',
customer_confirmation: '', customer_confirmation: '',
customer_feedback: '', // customer_feedback: '',
status: '', status: '',
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })

188
admin/src/app/views/statistics/home.vue

@ -0,0 +1,188 @@
<template>
<div class="main-container">
<!-- 实时概况 -->
<el-card shadow="never" class="!border-none">
<template #header>
<span class="text-lg font-extrabold mr-[10px]">统计概括</span>
<span class="text-sm text-[#a19f98]">查看校区员工资源和学员的实时数据</span>
</template>
<el-row :gutter="20">
<el-col :span="6">
<div class="text-sm text-[#a19f98] leading-8 ">
<el-statistic :value="info.campus_count">
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">校区数量</span>
</div>
</template>
</el-statistic>
<div class="text-sm text-[#a19f98] leading-8">
</div>
</div>
</el-col>
<el-col :span="6">
<div class="text-sm text-[#a19f98] leading-8 ">
<el-statistic :value="info.personnel_count">
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">员工数量</span>
</div>
</template>
</el-statistic>
<div class="text-sm text-[#a19f98] leading-8">
</div>
</div>
</el-col>
<el-col :span="6">
<div class="text-sm text-[#a19f98] leading-8 ">
<el-statistic :value="info.customerResources_count">
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">资源数量</span>
</div>
</template>
</el-statistic>
<div class="text-sm text-[#a19f98] leading-8">
<span>昨日新增</span>
<span>{{info.customerResources_day_count}}</span>
</div>
</div>
</el-col>
<el-col :span="6">
<div class="text-sm text-[#a19f98] leading-8 ">
<el-statistic :value="info.student_count">
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">学员数量</span>
</div>
</template>
</el-statistic>
<div class="text-sm text-[#a19f98] leading-8">
<span>昨日新增</span>
<span>{{info.student_day_count}}</span>
</div>
</div>
</el-col>
</el-row>
</el-card>
<!-- 实时概况 end -->
<el-row :gutter="15" class="mt-[15px]">
<el-col :span="24">
<el-card shadow="never" class="!border-none">
<template #header>
<span class="text-lg font-extrabold">资源增长趋势</span>
</template>
<div >
<el-button :type="activeRange === 'week' ? 'primary' : 'default'" size="small"
@click="changeRange('week')"></el-button>
<el-button :type="activeRange === 'month' ? 'primary' : 'default'" size="small"
@click="changeRange('month')"></el-button>
<el-button :type="activeRange === 'year' ? 'primary' : 'default'" size="small"
@click="changeRange('year')"></el-button>
</div>
<div ref="visitStat" :style="{ width: '100%', height: '300px' }">
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { getHome } from '@/app/api/sys'
import * as echarts from 'echarts'
const visitStat = ref<any>(null)
const activeRange = ref<'week' | 'month' | 'year'>('week')
const info = reactive({
campus_count: 0,
personnel_count: 0,
customerResources_count: 0,
customerResources_day_count: 0,
student_count: 0,
student_day_count: 0,
customer: []
})
const changeRange = (range) => {
activeRange.value = range
Init(range) //
}
const Init = (range) => {
getHome({'date':range})
.then((res) => {
info.campus_count = res.data.campus_count
info.personnel_count = res.data.personnel_count
info.customerResources_count = res.data.customerResources_count
info.customerResources_day_count = res.data.customerResources_day_count
info.student_count = res.data.student_count
info.student_day_count = res.data.student_day_count
info.customer = res.data.customer
drawChart('');
})
.catch(() => {
})
}
Init();
const drawChart = (item : any) => {
let value = info.customer.value
if (item) value = item
if (!visitStat.value) return
const visitStatChart = echarts.init(visitStat.value)
const visitStatOption = ref({
// title: {
// text: ''
// },
legend: {},
xAxis: {
data: []
},
yAxis: {},
tooltip: {
trigger: 'axis'
},
series: [
{
type: 'line',
data: []
}
]
})
visitStatOption.value.xAxis.data = info.customer.time
visitStatOption.value.series[0].data = value
visitStatChart.setOption(visitStatOption.value)
}
</script>
<style lang="scss" scoped></style>

2
admin/src/app/views/student/components/student-edit.vue

@ -48,7 +48,7 @@
v-for="(item, index) in genderList" v-for="(item, index) in genderList"
:key="index" :key="index"
:label="item.name" :label="item.name"
:value="item.value" :value="Number(item.value)"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>

19
admin/src/app/views/student/student.vue

@ -18,7 +18,7 @@
v-for="(item, index) in campusIdList" v-for="(item, index) in campusIdList"
:key="index" :key="index"
:label="item['campus_name']" :label="item['campus_name']"
:value="item['id']" :value="Number(item['id'])"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -68,12 +68,12 @@
<template #empty> <template #empty>
<span>{{ !studentTable.loading ? t('emptyData') : '' }}</span> <span>{{ !studentTable.loading ? t('emptyData') : '' }}</span>
</template> </template>
<el-table-column prop="user_id_name" :label="t('userId')" min-width="120" :show-overflow-tooltip="true"/> <!-- <el-table-column prop="user_id_name" :label="t('userId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="campus_id_name" :label="t('campusId')" min-width="120" :show-overflow-tooltip="true"/> <el-table-column prop="campus_id_name" :label="t('campusId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="class_id_name" :label="t('classId')" min-width="120" :show-overflow-tooltip="true"/> <el-table-column prop="class_id_name" :label="t('classId')" min-width="120" :show-overflow-tooltip="true"/>
-->
<el-table-column prop="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/> <el-table-column prop="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('gender')" min-width="180" align="center" :show-overflow-tooltip="true"> <el-table-column :label="t('gender')" min-width="180" align="center" :show-overflow-tooltip="true">
@ -122,6 +122,7 @@
<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="classEvent(row.getInfoRel)">班级分配</el-button>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button> <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button> <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template> </template>
@ -136,6 +137,8 @@
</div> </div>
<edit ref="editStudentDialog" @complete="loadStudentList" /> <edit ref="editStudentDialog" @complete="loadStudentList" />
<class ref="classStudentDialog" @complete="loadStudentList" />
</el-card> </el-card>
</div> </div>
</template> </template>
@ -148,6 +151,7 @@ import { getStudentList, deleteStudent, getWithCustomerResourcesList, getWithCam
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/student/components/student-edit.vue' import Edit from '@/app/views/student/components/student-edit.vue'
import Class from '@/app/views/class_resources_rel/components/class-resources-rel-edit.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;
@ -232,6 +236,15 @@ const editEvent = (data: any) => {
editStudentDialog.value.showDialog = true editStudentDialog.value.showDialog = true
} }
const classStudentDialog: Record<string, any> | null = ref(null)
const classEvent = (data: any) => {
classStudentDialog.value.setFormData(data)
classStudentDialog.value.showDialog = true
}
/** /**
* 删除学员 * 删除学员
*/ */

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

@ -70,12 +70,15 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="位置选择" v-if="form.venue_id && form.time_slot && form.course_type === 'class'"> <el-form-item label="位置选择" v-if="form.venue_id">
<SeatSelector <SeatSelector
:venueId="form.venue_id" :venueId="form.venue_id"
:capacity="venueCapacity" :capacity="maxSeatSelections"
v-model:selectedSeats="form.selectedSeats" v-model:selectedSeats="form.selectedSeats"
:selected-seat-ids="selectedStudentSeats"
:max-selections="maxSeatSelections"
@change="handleSeatSelectionChange" @change="handleSeatSelectionChange"
@update:selected-seat-ids="updateSelectedStudentSeats"
/> />
</el-form-item> </el-form-item>
@ -98,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>
@ -125,7 +150,6 @@ import { ref, defineProps, defineEmits, watch } from 'vue'
import {getAllVenueList} from '@/app/api/venue' import {getAllVenueList} from '@/app/api/venue'
import { getAllClassroomList, getClassroompeople, getWithPersonnelList, getClassroompeopleCount } 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' import SeatSelector from './seat-selector.vue'
@ -151,11 +175,15 @@ const studentList = ref([])
const coachList = ref([]) const coachList = ref([])
const timeSlotOptions = ref([]) const timeSlotOptions = ref([])
const availableCapacity = ref(0) const availableCapacity = ref(0)
const venueCapacity = ref(0)
const studentSearchKeyword = ref('') const studentSearchKeyword = ref('')
const filteredStudentList = ref([]) const filteredStudentList = ref([])
const occupiedSeats = ref([]) const occupiedSeats = ref([])
const participants = ref([]) // getClassroompeopleCount 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) => {
@ -170,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()
}
} }
} }
}) })
@ -228,8 +260,12 @@ const handleCampusChange = async () => {
form.value.time_slot = '' form.value.time_slot = ''
timeSlotOptions.value = [] timeSlotOptions.value = []
availableCapacity.value = 0 availableCapacity.value = 0
venueCapacity.value = 0
occupiedSeats.value = [] occupiedSeats.value = []
//
if (form.value.course_type === 'class') {
handleCourseTypeChange()
}
} }
// //
@ -303,12 +339,12 @@ const generateTimeSlots = (venueInfo) => {
const startTotalMinutes = startHour * 60 + startMinute const startTotalMinutes = startHour * 60 + startMinute
const endTotalMinutes = endHour * 60 + endMinute const endTotalMinutes = endHour * 60 + endMinute
// 10 // 60
for (let minutes = startTotalMinutes; minutes < endTotalMinutes - 9; minutes += 10) { for (let minutes = startTotalMinutes; minutes < endTotalMinutes; minutes += 60) {
const startHour = Math.floor(minutes / 60) const startHour = Math.floor(minutes / 60)
const startMinute = minutes % 60 const startMinute = minutes % 60
const endHour = Math.floor((minutes + 10) / 60) const endHour = Math.floor((minutes + 60) / 60)
const endMinute = (minutes + 10) % 60 const endMinute = (minutes + 60) % 60
const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}` const startTime = `${startHour.toString().padStart(2, '0')}:${startMinute.toString().padStart(2, '0')}`
const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}` const endTime = `${endHour.toString().padStart(2, '0')}:${endMinute.toString().padStart(2, '0')}`
@ -331,117 +367,121 @@ const handleVenueChange = () => {
const selectedVenue = venueList.value.find(item => item.id === form.value.venue_id) const selectedVenue = venueList.value.find(item => item.id === form.value.venue_id)
if (selectedVenue) { if (selectedVenue) {
timeSlotOptions.value = generateTimeSlots(selectedVenue) timeSlotOptions.value = generateTimeSlots(selectedVenue)
venueCapacity.value = selectedVenue.capacity || 0 //
maxSeatSelections.value = selectedVenue.capacity || 30
console.log('场地容量:', selectedVenue.capacity, '最大选择数:', maxSeatSelections.value)
} else { } else {
timeSlotOptions.value = [] timeSlotOptions.value = []
venueCapacity.value = 0 maxSeatSelections.value = 30
console.log('未找到场地,使用默认容量:', maxSeatSelections.value)
} }
availableCapacity.value = 0 availableCapacity.value = 0
occupiedSeats.value = [] occupiedSeats.value = []
//
form.value.selectedSeats = []
selectedStudentSeats.value = []
} }
//
const calculateAvailableCapacity = async () => {
if (!form.value.venue_id || !form.value.time_slot) {
availableCapacity.value = 0
occupiedSeats.value = []
return
}
try { //
// const handleSeatSelectionChange = (selectedSeats) => {
const venueInfo = venueList.value.find(item => item.id === form.value.venue_id) console.log('选中的座位:', selectedSeats);
if (venueInfo) {
//
availableCapacity.value = venueInfo.capacity || 0
//
if (form.value.course_date) {
const params = {
venue_id: form.value.venue_id,
course_date: form.value.course_date,
time_slot: form.value.time_slot
}
const response = await getTimetables(params) //
if (response.data && response.data.length > 0) { if (form.value.course_type === 'student' || form.value.course_type === 'trial') {
// const students = studentList.value.filter(student => form.value.student_ids.includes(student.id));
const matchingDay = response.data.find(day =>
day.date.includes(form.value.course_date)
)
if (matchingDay) { //
const matchingTimeSlot = matchingDay.timeSlots.find(slot => selectedStudentSeats.value = selectedSeats.map((seatId, index) => {
slot.timeRange === form.value.time_slot const student = students[index];
) return {
id: seatId,
name: student ? student.name : ''
};
});
}
};
if (matchingTimeSlot && matchingTimeSlot.course) { //
// const updateSelectedStudentSeats = (seatObjects) => {
availableCapacity.value = matchingTimeSlot.course.hasnumber selectedStudentSeats.value = seatObjects;
console.log('选中的学生座位:', selectedStudentSeats.value);
};
// API //
if (matchingTimeSlot.course.occupiedSeats) { const handleClassStudentSelectionChange = (studentId, checked) => {
occupiedSeats.value = matchingTimeSlot.course.occupiedSeats //
} else { const studentIndex = classStudentList.value.findIndex(s => s.id === studentId);
// if (studentIndex !== -1) {
const totalSeats = venueCapacity.value; classStudentList.value[studentIndex].checked = checked;
const occupiedCount = totalSeats - availableCapacity.value;
occupiedSeats.value = generateRandomOccupiedSeats(occupiedCount); //
} 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);
} }
} catch (error) {
console.error('计算可用容量失败:', error)
} }
}
// console.log('选中的班级学员IDs:', form.value.student_ids);
const generateRandomOccupiedSeats = (count) => {
const occupied = [];
const rows = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
const cols = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (let i = 0; i < count; i++) {
const row = rows[Math.floor(Math.random() * rows.length)];
const col = cols[Math.floor(Math.random() * cols.length)];
const seat = `${row}-${col}`;
if (!occupied.includes(seat)) {
occupied.push(seat);
}
} }
return occupied;
};
//
const handleSeatSelectionChange = (selectedSeats) => {
console.log('选中的座位:', selectedSeats);
}; };
// //
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)
if (response.data && Array.isArray(response.data)) {
// API
classStudentList.value = response.data.map(student => ({
id: student.id,
name: student.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)
// //
const handleClassChange = () => { classStudentList.value = Array.from({ length: 20 }, (_, i) => ({
// id: i + 1,
if (form.value.class_ids.length > 0) { name: `班级学员${i + 1}`,
const selectedClass = classList.value.find(item => item.id === form.value.class_ids[0]) checked: false
if (selectedClass) { }))
form.value.course_name = selectedClass.class_name filteredClassStudentList.value = [...classStudentList.value]
} }
} }
} }
@ -500,6 +540,18 @@ 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
@ -511,10 +563,6 @@ const handleTimeSlotChange = async () => {
participants.value = [] participants.value = []
return return
} }
//
await calculateAvailableCapacity()
// //
try { try {
const params = { const params = {
@ -560,15 +608,12 @@ const submit = () => {
participants_info: participants.value // 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)
@ -599,6 +644,12 @@ const resetForm = () => {
coach_id: '', coach_id: '',
selectedSeats: [] 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()
} }
@ -614,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>

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

@ -1,21 +1,31 @@
<template> <template>
<div class="seat-selector"> <div class="seat-selector">
<div class="seats-container"> <div class="seats-container">
<div v-for="row in rows" :key="row" class="seat-row"> <div v-if="rows.length === 0" class="no-seats-message">
<div class="row-label">{{ row }}</div> 没有可用座位请确认场地容量设置
</div>
<div v-else v-for="row in rows" :key="row" class="seat-row">
<div <div
v-for="col in cols" v-for="col in cols"
:key="`${row}-${col}`" :key="`${row}-${col}`"
class="seat-container"
>
<div
class="seat" class="seat"
:class="{ :class="{
'seat-available': isSeatAvailable(row, col), 'seat-available': isSeatAvailable(row, col),
'seat-occupied': !isSeatAvailable(row, col), 'seat-occupied': !isSeatAvailable(row, col) && isSeatValid(row, col),
'seat-selected': isSelected(row, col) 'seat-selected': isSelected(row, col),
'seat-hidden': !isSeatValid(row, col)
}" }"
@click="toggleSeat(row, col)" @click="toggleSeat(row, col)"
> >
{{ col }} {{ col }}
</div> </div>
<div v-if="isSelected(row, col)" class="seat-name">
{{ getSeatObject(row, col)?.name || '' }}
</div>
</div>
</div> </div>
</div> </div>
<div class="seat-legend"> <div class="seat-legend">
@ -33,7 +43,7 @@
</div> </div>
</div> </div>
<div class="seat-info"> <div class="seat-info">
<span>已选: {{ selectedSeats.length }}</span> <span>已选: {{ selectedSeats.length }}/{{ props.maxSelections === Infinity ? '不限' : props.maxSelections }}</span>
<span>剩余空位: {{ availableSeats.length }}</span> <span>剩余空位: {{ availableSeats.length }}</span>
</div> </div>
</div> </div>
@ -42,6 +52,7 @@
<script setup> <script setup>
import { ref, computed, watch, onMounted } from 'vue'; import { ref, computed, watch, onMounted } from 'vue';
import { getClassroompeople } from '@/app/api/classroom'; import { getClassroompeople } from '@/app/api/classroom';
import { getAllVenueList } from '@/app/api/venue';
const props = defineProps({ const props = defineProps({
venueId: { venueId: {
@ -55,54 +66,48 @@ const props = defineProps({
occupiedSeats: { occupiedSeats: {
type: Array, type: Array,
default: () => [] default: () => []
},
selectedSeatIds: {
type: Array,
default: () => []
},
maxSelections: {
type: Number,
default: Infinity
} }
}); });
const emit = defineEmits(['update:selectedSeats', 'change']); const emit = defineEmits(['update:selectedSeats', 'change', 'update:selectedSeatIds']);
// //
const rows = ref(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']); const rows = ref([]);
const cols = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); const cols = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); // 10
const selectedSeats = ref([]); const selectedSeats = ref([]);
const occupiedSeatsData = ref([]); const occupiedSeatsData = ref([]);
const selectedSeatObjects = ref([]);
const totalSeats = ref(0);
// // - 使onMountedwatch
const adjustLayout = () => { const adjustLayout = (capacityValue) => {
if (!props.capacity) return; console.log('已废弃的adjustLayout调用,使用值:', capacityValue);
//
const totalCapacity = props.capacity;
let colCount = Math.ceil(Math.sqrt(totalCapacity));
if (colCount > 12) colCount = 12; //
const rowCount = Math.ceil(totalCapacity / colCount);
// A-Z
rows.value = Array.from({ length: rowCount }, (_, i) =>
String.fromCharCode(65 + i)
).slice(0, 26); // 26A-Z
// 1-N
cols.value = Array.from({ length: colCount }, (_, i) => i + 1);
}; };
// //
const loadOccupiedSeats = async () => { const loadVenueInfoAndOccupiedSeats = async () => {
if (!props.venueId) return; if (!props.venueId) return;
try { try {
// 使 // 使
if (props.occupiedSeats && props.occupiedSeats.length) { if (props.occupiedSeats && props.occupiedSeats.length) {
occupiedSeatsData.value = props.occupiedSeats; occupiedSeatsData.value = props.occupiedSeats;
return; } else {
}
// API // API
const response = await getClassroompeople(props.venueId); const response = await getClassroompeople(props.venueId);
if (response.data && Array.isArray(response.data)) { if (response.data && Array.isArray(response.data)) {
// API // API
occupiedSeatsData.value = response.data; occupiedSeatsData.value = response.data;
} }
}
} catch (error) { } catch (error) {
console.error('获取已占用位置失败:', error); console.error('获取已占用位置失败:', error);
// //
@ -128,22 +133,54 @@ const generateRandomOccupiedSeats = () => {
return occupied; return occupied;
}; };
// //
const availableSeats = computed(() => { const allValidSeats = computed(() => {
const allSeats = []; const seats = [];
if (rows.value.length === 0) return seats;
rows.value.forEach(row => { rows.value.forEach(row => {
cols.value.forEach(col => { cols.value.forEach(col => {
if (isSeatValid(row, col)) {
const seatId = `${row}-${col}`; const seatId = `${row}-${col}`;
if (!occupiedSeatsData.value.includes(seatId)) { seats.push(seatId);
allSeats.push(seatId);
} }
}); });
}); });
return allSeats;
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) => { const isSeatAvailable = (row, col) => {
//
if (!isSeatValid(row, col)) {
return false;
}
const seatId = `${row}-${col}`; const seatId = `${row}-${col}`;
return !occupiedSeatsData.value.includes(seatId); return !occupiedSeatsData.value.includes(seatId);
}; };
@ -154,40 +191,169 @@ const isSelected = (row, col) => {
return selectedSeats.value.includes(seatId); 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) => { const toggleSeat = (row, col) => {
console.log('点击座位:', row, col);
//
if (!isSeatValid(row, col)) {
console.log('座位无效,不能选择');
return;
}
const seatId = `${row}-${col}`; const seatId = `${row}-${col}`;
if (!isSeatAvailable(row, col)) return; //
//
if (!isSeatAvailable(row, col)) {
console.log('座位已占用,不能选择');
return;
}
const index = selectedSeats.value.indexOf(seatId); const index = selectedSeats.value.indexOf(seatId);
if (index === -1) { if (index === -1) {
//
if (selectedSeats.value.length >= props.maxSelections) {
console.log('已达到最大选择数量:', props.maxSelections);
return;
}
console.log('添加座位:', seatId);
selectedSeats.value.push(seatId); selectedSeats.value.push(seatId);
//
if (!selectedSeatObjects.value.some(obj => obj.id === seatId)) {
selectedSeatObjects.value.push({ id: seatId, name: '' });
}
} else { } else {
console.log('移除座位:', seatId);
selectedSeats.value.splice(index, 1); 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:selectedSeats', selectedSeats.value);
emit('update:selectedSeatIds', selectedSeatObjects.value);
emit('change', selectedSeats.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, () => { watch(() => props.venueId, async () => {
console.log('监听venueId变化:', props.venueId);
selectedSeats.value = []; selectedSeats.value = [];
loadOccupiedSeats(); 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, () => { //
adjustLayout(); 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, () => { watch(() => props.occupiedSeats, () => {
occupiedSeatsData.value = 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(() => { onMounted(async () => {
adjustLayout(); console.log('组件挂载 - venueId:', props.venueId, 'capacity:', props.capacity);
loadOccupiedSeats();
//
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> </script>
@ -225,7 +391,7 @@ onMounted(() => {
.seat-row { .seat-row {
display: flex; display: flex;
align-items: center; align-items: flex-start;
gap: 10px; gap: 10px;
} }
@ -233,6 +399,15 @@ onMounted(() => {
width: 20px; width: 20px;
text-align: center; text-align: center;
font-weight: bold; font-weight: bold;
margin-top: 10px;
}
.seat-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
margin-bottom: 5px;
} }
.seat { .seat {
@ -247,6 +422,16 @@ onMounted(() => {
transition: all 0.2s; 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 { .seat-available {
background-color: #67c23a; background-color: #67c23a;
color: white; color: white;
@ -263,6 +448,11 @@ onMounted(() => {
color: white; color: white;
} }
.seat-hidden {
visibility: hidden;
pointer-events: none;
}
.seat-legend { .seat-legend {
display: flex; display: flex;
gap: 20px; gap: 20px;
@ -286,4 +476,11 @@ onMounted(() => {
gap: 20px; gap: 20px;
font-size: 14px; font-size: 14px;
} }
.no-seats-message {
font-size: 14px;
color: #666;
padding: 20px;
text-align: center;
}
</style> </style>

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

@ -1,12 +1,14 @@
<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="header-control-panel mb-4">
<div class="flex items-center"> <div class="flex items-center flex-wrap">
<el-select <el-select
v-model="selectedCampus" v-model="selectedCampus"
placeholder="请选择校区" placeholder="请选择校区"
clearable clearable
class="mr-2" size="default"
class="mr-2 mb-2"
style="width: 160px;"
@change="handleCampusChange" @change="handleCampusChange"
> >
<el-option <el-option
@ -16,19 +18,24 @@
:value="item.id" :value="item.id"
/> />
</el-select> </el-select>
<el-button @click="prevWeek" icon="el-icon-arrow-left">上一周</el-button> <el-button size="default" @click="prevWeek" icon="el-icon-arrow-left" class="mb-2">上一周</el-button>
<div class="mx-2 mb-2">
<el-date-picker <el-date-picker
v-model="weekDate" v-model="weekDate"
type="week" type="week"
format="YYYY 第 ww 周" format="YYYY 第 ww 周"
placeholder="选择周" placeholder="选择周"
class="week-picker" size="default"
style="width: 180px;"
@change="handleWeekChange" @change="handleWeekChange"
/> />
<el-button @click="nextWeek" icon="el-icon-arrow-right">下一周</el-button>
<el-button type="primary" class="ml-2" @click="fetchData">查询</el-button>
</div> </div>
<el-button @click="addSchedule">添加课程</el-button> <el-button size="default" @click="nextWeek" icon="el-icon-arrow-right" class="mb-2">下一周</el-button>
<el-button type="primary" size="default" class="ml-2 mb-2" @click="fetchData">查询</el-button>
</div>
<div>
<el-button size="default" type="primary" plain @click="addSchedule">添加课程</el-button>
</div>
</div> </div>
<div class="schedule-container"> <div class="schedule-container">
@ -39,7 +46,10 @@
:data="day.timeSlots" :data="day.timeSlots"
border border
:span-method="(data) => objectSpanMethod(day.timeSlots, data)" :span-method="(data) => objectSpanMethod(day.timeSlots, data)"
style="width: 100%" style="width: 100%; height: 100%;"
max-height="100%"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
:cell-style="{ padding: '4px' }"
@cell-click="handleCellClick" @cell-click="handleCellClick"
> >
<!-- 时间列 --> <!-- 时间列 -->
@ -49,37 +59,40 @@
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"
width="280"
> >
<template #default="{ row }"> <template #default="{ row }">
<div v-if="row.course && row.course.classroom == classroom"> <div v-if="row.course && row.course.classroom[0].id == classroom.id" class="course-cell" :style="{ backgroundColor: row.backgroundColor || '#f0f9eb', color: row.color ? '#fff' : '#000' }">
<div class="teacher-name"> <div class="teacher-name">
{{ getTeacherName(row.course.teacher) }} {{ row.course.teacher[0].name }}
</div> </div>
<div class="student-list"> <div class="student-list" :style="{ backgroundColor: row.backgroundColor || '#f0f9eb', color: row.color ? '#fff' : '#000' }">
<el-tag <div
v-for="student in row.course.students" v-for="student in row.course.students"
:key="student" :key="student.id"
size="small" class="custom-student-tag"
effect="plain"
> >
{{ getStudentName(student) }} <div class="tag-content">
</el-tag> <span class="tag-label">家长</span>
<span class="tag-value">{{ student.resources?.name || '未知' }}</span>
<template v-if="student.student && student.student?.name">
<span class="tag-divider">|</span>
<span class="tag-label">学员</span>
<span class="tag-value">{{ student.student?.name }}</span>
</template>
</div>
</div>
</div> </div>
<div class="classroom-name"> <div class="classroom-name" :style="{ backgroundColor: row.backgroundColor || '#f0f9eb', color: row.color ? '#fff' : '#000' }">
剩余空位{{ row.course.hasnumber }} 剩余空位{{ row.course.hasnumber }}
</div> </div>
</div> </div>
@ -89,16 +102,61 @@
</div> </div>
<!-- 详情弹窗 --> <!-- 详情弹窗 -->
<el-dialog v-model="dialogVisible" title="课程详情"> <el-dialog v-model="dialogVisible" title="课程安排" width="600px" @open="handleDialogOpen">
<p> <!-- 人员类型选择 -->
<strong>教师:</strong> {{ getTeacherName(selectedCourse?.teacher) }} <div class="my-3">
</p> <el-radio-group v-model="personType" @change="handlePersonTypeChange">
<p> <el-radio :label="'course'">课程人员</el-radio>
<strong>学员:</strong> <el-radio :label="'trial'">试课人员</el-radio>
{{ </el-radio-group>
selectedCourse?.students.map((id) => getStudentName(id)).join(', ') </div>
}}
</p> <!-- 试课人员搜索框 -->
<div v-if="personType === 'trial'" class="search-box mb-3">
<el-input
v-model="searchKeyword"
placeholder="请输入姓名或手机号"
class="mr-2"
style="width: 220px;"
>
<template #append>
<el-button @click="searchPerson">
<el-icon><Search /></el-icon>
</el-button>
</template>
</el-input>
</div>
<!-- 人员列表多选区域 -->
<div class="student-checkbox-container">
<el-checkbox-group v-model="selectedStudentIds">
<el-checkbox
v-for="student in students"
:key="student.id"
:label="student.id"
class="student-checkbox-item"
:checked="student.checked === true"
@change="(val) => handleStudentCheck(student, val)"
>
<div class="student-info">
<span class="student-name">{{ student.name }}</span>
<span v-if="student.student_name" class="student-school">
({{ student.student_name }})
</span>
</div>
</el-checkbox>
</el-checkbox-group>
<div v-if="students.length === 0" class="text-center py-3 text-gray-500">
暂无人员数据
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveStudents">保存</el-button>
</span>
</template>
</el-dialog> </el-dialog>
<!-- 添加课程弹窗组件 --> <!-- 添加课程弹窗组件 -->
@ -113,9 +171,12 @@
<script setup> <script setup>
import { ref, onMounted, computed } from 'vue' import { ref, onMounted, computed } from 'vue'
import { getTimetables } from '@/app/api/course_schedule' import { getTimetables, getCourseStudents, getResourceByNameOrPhone } from '@/app/api/course_schedule'
import { addPersonCourseSchedule,getTryCoursePerson } from '@/app/api/person_course_schedule'
import { getWithCampusList } from '@/app/api/venue' import { getWithCampusList } from '@/app/api/venue'
import ScheduleAdd from './components/schedule-add.vue' import ScheduleAdd from './components/schedule-add.vue'
import { ElMessage } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
// //
const campusList = ref([]) const campusList = ref([])
@ -151,6 +212,15 @@ const days = ref([])
const dialogVisible = ref(false) const dialogVisible = ref(false)
const selectedCourse = ref(null) const selectedCourse = ref(null)
//
const personType = ref('course') //
const searchKeyword = ref('') //
const students = ref([]) //
const selectedStudentIds = ref([]) // ID
const selectedStudents = ref([]) //
const courseStudents = ref([]) //
const trialStudents = ref([]) //
// //
const prevWeek = () => { const prevWeek = () => {
const date = new Date(weekDate.value) const date = new Date(weekDate.value)
@ -188,8 +258,10 @@ const fetchCampusList = async () => {
selectedCampus.value = campusList.value[0].id selectedCampus.value = campusList.value[0].id
} }
} }
return Promise.resolve()
} catch (error) { } catch (error) {
console.error('获取校区列表失败:', error) console.error('获取校区列表失败:', error)
return Promise.reject(error)
} }
} }
@ -215,18 +287,6 @@ const fetchData = async () => {
} }
} }
//
const getTeacherName = (teacherId) => {
// teacherId
return teacherId ? `教师${teacherId}` : ''
}
//
const getStudentName = (studentId) => {
// studentId
return studentId ? `学生${studentId}` : ''
}
// //
const addSchedule = () => { const addSchedule = () => {
addDialogVisible.value = true addDialogVisible.value = true
@ -266,44 +326,286 @@ const objectSpanMethod = (timeSlots, { row, column, rowIndex }) => {
// //
const handleCellClick = (row, column, cell, event) => { const handleCellClick = (row, column, cell, event) => {
if (column.property.startsWith('classroom') && row.course) { if (column.property.startsWith('classroom') && row.course) {
selectedCourse.value = row.course console.log(row.course.schedule)
selectedCourse.value = row.course.schedule.id
dialogVisible.value = true dialogVisible.value = true
} else {
ElMessage.warning('当天该时间段没有课程')
}
}
//
const handleDialogOpen = async () => {
if (!selectedCourse.value) {
ElMessage.warning('未选择有效课程')
return
}
//
personType.value = 'course'
searchKeyword.value = ''
courseStudents.value = []
trialStudents.value = []
students.value = []
selectedStudentIds.value = []
selectedStudents.value = []
//
await fetchCourseStudents()
}
//
const fetchCourseStudents = async () => {
try {
if (!selectedCourse.value) return
const response = await getCourseStudents(selectedCourse.value)
if (response.data) {
courseStudents.value = response.data || []
students.value = courseStudents.value
// - checked
const checkedStudents = students.value.filter(student => student.checked === true)
selectedStudentIds.value = checkedStudents.map(student => student.id)
//
selectedStudents.value = checkedStudents.map(student => ({
id: student.id,
student_id: student.student_id || null,
name: student.name,
student_name: student.student_name || null
}))
}
} catch (error) {
console.error('获取课程人员列表失败:', error)
ElMessage.error('获取课程人员列表失败')
courseStudents.value = []
students.value = []
selectedStudentIds.value = []
selectedStudents.value = []
}
}
//
const handleStudentCheck = (student, checked) => {
if (checked) {
//
if (!selectedStudents.value.some(item => item.id === student.id)) {
selectedStudents.value.push({
id: student.id,
student_id: student.student_id || null,
name: student.name,
student_name: student.student_name || null
})
}
} else {
//
const index = selectedStudents.value.findIndex(item => item.id === student.id)
if (index !== -1) {
selectedStudents.value.splice(index, 1)
}
}
}
//
const handlePersonTypeChange = async () => {
//
searchKeyword.value = ''
if (personType.value === 'course') {
//
students.value = courseStudents.value
} else {
// getTryCoursePerson
try {
if (selectedCourse.value) {
const response = await getTryCoursePerson(selectedCourse.value)
if (response.data) {
trialStudents.value = response.data || []
students.value = trialStudents.value
//
const checkedStudents = students.value.filter(student => student.checked === true)
if (checkedStudents.length > 0) {
checkedStudents.forEach(student => {
if (!selectedStudentIds.value.includes(student.id)) {
selectedStudentIds.value.push(student.id)
//
if (!selectedStudents.value.some(item => item.id === student.id)) {
selectedStudents.value.push({
id: student.id,
student_id: student.student_id || null,
name: student.name,
student_name: student.student_name || null
})
}
}
})
}
}
} else {
//
trialStudents.value = []
students.value = trialStudents.value
}
} catch (error) {
console.error('获取试课人员失败:', error)
ElMessage.error('获取试课人员失败')
trialStudents.value = []
students.value = trialStudents.value
}
}
}
//
const searchPerson = async () => {
if (!searchKeyword.value.trim()) {
ElMessage.warning('请输入搜索关键词')
return
}
try {
const response = await getResourceByNameOrPhone({
name: searchKeyword.value.trim()
})
if (response.data) {
// ID
const currentSelectedIds = [...selectedStudentIds.value]
trialStudents.value = response.data || []
students.value = trialStudents.value
// checkedtrue
//
const checkedStudents = students.value.filter(student => student.checked === true)
// 使
selectedStudentIds.value = currentSelectedIds
// checked
if (checkedStudents.length > 0) {
checkedStudents.forEach(student => {
if (!selectedStudentIds.value.includes(student.id)) {
selectedStudentIds.value.push(student.id)
//
if (!selectedStudents.value.some(item => item.id === student.id)) {
selectedStudents.value.push({
id: student.id,
student_id: student.student_id || null,
name: student.name,
student_name: student.student_name || null
})
}
}
})
}
}
} catch (error) {
console.error('搜索人员失败:', error)
ElMessage.error('搜索人员失败')
}
}
//
const saveStudents = async () => {
if (selectedStudentIds.value.length === 0) {
ElMessage.warning('请至少选择一名人员')
return
}
try {
//
await addPersonCourseSchedule({
schedule_id: selectedCourse.value,
resources_id: selectedStudentIds.value,
// student_id
student_ids: selectedStudents.value
.filter(student => student.student_id)
.map(student => student.student_id)
})
ElMessage.success('保存成功')
dialogVisible.value = false
//
fetchData()
} catch (error) {
console.error('保存人员失败:', error)
ElMessage.error('保存人员失败')
} }
} }
// //
onMounted(() => { onMounted(() => {
fetchCampusList() fetchCampusList().then(() => {
fetchData() fetchData()
})
}) })
</script> </script>
<style scoped> <style scoped>
.header-control-panel {
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
}
.schedule-container { .schedule-container {
display: flex; display: flex;
gap: 10px; gap: 5px;
overflow-x: auto; overflow-x: auto;
background-color: #fff;
padding: 0;
border-radius: 4px;
} }
.day-column { .day-column {
flex: 1; flex: 1;
min-width: 200px; min-width: 180px;
display: flex;
flex-direction: column;
} }
.day-header { .day-header {
text-align: center; text-align: center;
font-weight: bold; font-weight: bold;
padding: 8px; padding: 8px 0;
background-color: #f0f0f0; background-color: #f5f7fa;
border-top: 1px solid #ebeef5;
border-left: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
color: #606266;
font-size: 14px;
} }
.teacher-name { .teacher-name {
font-weight: bold; font-weight: bold;
margin-bottom: 5px; margin-bottom: 3px;
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.student-list { .student-list {
margin-top: 5px; margin: 3px 0;
max-height: 60px;
overflow-y: auto;
display: flex;
flex-wrap: wrap;
}
.el-table {
height: calc(100vh - 220px);
overflow-y: auto;
font-size: 13px;
border-collapse: collapse;
} }
.el-table__cell { .el-table__cell {
@ -312,10 +614,129 @@ onMounted(() => {
} }
.classroom-name { .classroom-name {
margin-bottom: 5px; margin-top: 3px;
font-size: 12px;
color: #606266;
}
.course-cell {
padding: 4px;
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
border-radius: 3px;
}
.custom-student-tag {
background-color: #ecf5ff;
color: #409eff;
border: 1px solid #d9ecff;
border-radius: 4px;
padding: 0 8px;
margin: 2px;
font-size: 12px;
display: inline-block;
max-width: 260px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
box-sizing: border-box;
height: 24px;
line-height: 22px;
transition: all .2s;
}
.custom-student-tag:hover {
background-color: #d9ecff;
color: #3a8ee6;
cursor: default;
}
.tag-content {
display: flex;
flex-wrap: nowrap;
align-items: center;
max-width: 100%;
overflow: hidden;
}
.tag-label {
font-weight: bold;
font-size: 11px;
flex-shrink: 0;
}
.tag-value {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.tag-divider {
margin: 0 2px;
color: #99c2ff;
flex-shrink: 0;
}
:deep(.el-table__body-wrapper) {
overflow-y: auto;
height: 100%;
}
:deep(.el-table__header-wrapper) {
position: sticky;
top: 0;
z-index: 2;
}
:deep(.el-table--border) {
border-color: #ebeef5;
}
:deep(.el-table td), :deep(.el-table th) {
padding: 4px 0;
}
:deep(.el-table--enable-row-hover .el-table__body tr:hover > td) {
background-color: #f5f7fa;
}
.student-checkbox-container {
max-height: 300px;
overflow-y: auto;
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 10px;
margin-bottom: 15px;
}
.student-checkbox-item {
margin-right: 10px;
margin-bottom: 8px;
display: inline-block;
}
.student-info {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
}
.student-name {
font-weight: 500;
} }
.week-picker { .student-school {
width: 180px; color: #666;
margin-left: 4px;
font-size: 0.9em;
}
.search-box {
display: flex;
align-items: center;
} }
</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
}

BIN
niucloud/app.zip

Binary file not shown.

8
niucloud/app/adminapi/controller/campus_person_role/CampusPersonRole.php

@ -29,7 +29,7 @@ class CampusPersonRole extends BaseAdminController
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["campus_id",""], ["campus_id",""],
["person_id",""], ["person_name",""],
["role_id",""], ["role_id",""],
["dept_id",""] ["dept_id",""]
]); ]);
@ -100,7 +100,11 @@ class CampusPersonRole extends BaseAdminController
} }
public function getSysRoleAll(){ public function getSysRoleAll(){
return success(( new CampusPersonRoleService())->getSysRoleAll()); $data = $this->request->params([
["dept_id",0]
]);
return success(( new CampusPersonRoleService())->getSysRoleAll($data));
} }
public function getDepartmentsAll(){ public function getDepartmentsAll(){

104
niucloud/app/adminapi/controller/class_resources_rel/ClassResourcesRel.php

@ -0,0 +1,104 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\adminapi\controller\class_resources_rel;
use core\base\BaseAdminController;
use app\service\admin\class_resources_rel\ClassResourcesRelService;
/**
* 班级和资源控制器
* Class ClassResourcesRel
* @package app\adminapi\controller\class_resources_rel
*/
class ClassResourcesRel extends BaseAdminController
{
/**
* 获取班级和资源列表
* @return \think\Response
*/
public function lists(){
$data = $this->request->params([
]);
return success((new ClassResourcesRelService())->getPage($data));
}
/**
* 班级和资源详情
* @param int $id
* @return \think\Response
*/
public function info(int $id){
return success((new ClassResourcesRelService())->getInfo($id));
}
/**
* 添加班级和资源
* @return \think\Response
*/
public function add(){
$data = $this->request->params([
["class_id",0],
["resource_id",0],
["campus_id",0],
["source_type",""],
["status",0],
]);
$this->validate($data, 'app\validate\class_resources_rel\ClassResourcesRel.add');
$id = (new ClassResourcesRelService())->add($data);
return success('ADD_SUCCESS', ['id' => $id]);
}
/**
* 班级和资源编辑
* @param $id 班级和资源id
* @return \think\Response
*/
public function edit(int $id){
$data = $this->request->params([
["class_id",0],
["campus_id",0],
["source_type",""],
["status",0],
]);
// $this->validate($data, 'app\validate\class_resources_rel\ClassResourcesRel.edit');
(new ClassResourcesRelService())->edit($id, $data);
return success('EDIT_SUCCESS');
}
/**
* 班级和资源删除
* @param $id 班级和资源id
* @return \think\Response
*/
public function del(int $id){
(new ClassResourcesRelService())->del($id);
return success('DELETE_SUCCESS');
}
public function getClassGradeAll(){
return success(( new ClassResourcesRelService())->getClassGradeAll());
}
public function getCustomerResourcesAll(){
return success(( new ClassResourcesRelService())->getCustomerResourcesAll());
}
public function getCampusAll(){
return success(( new ClassResourcesRelService())->getCampusAll());
}
}

4
niucloud/app/adminapi/controller/communication_records/CommunicationRecords.php

@ -28,7 +28,9 @@ class CommunicationRecords extends BaseAdminController
*/ */
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["resource_id",""], ["name",""],
["phone_number",""],
["campus",""],
["communication_type",""] ["communication_type",""]
]); ]);
return success((new CommunicationRecordsService())->getPage($data)); return success((new CommunicationRecordsService())->getPage($data));

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

@ -26,17 +26,18 @@ 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,18 +80,19 @@ 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",""],
]); ]);
$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);
@ -100,9 +104,30 @@ 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(){
return success((new CourseService())->contract_all());
}
} }

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

@ -26,18 +26,19 @@ 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", ""],
['auto_schedule', 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", ""],
['auto_schedule', 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,7 +109,8 @@ 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');
} }
@ -114,7 +119,8 @@ class CourseSchedule extends BaseAdminController
* 获取课程表数据 * 获取课程表数据
* @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", ""],
@ -123,6 +129,7 @@ class CourseSchedule extends BaseAdminController
]); ]);
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([

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

@ -26,16 +26,17 @@ 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,56 @@ 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']));
}
public function getResourceByCourse($schedule)
{
return success((new CustomerResourcesService())->getResourceByCourse($schedule));
}
} }

5
niucloud/app/adminapi/controller/exam_papers/ExamPapers.php

@ -34,6 +34,11 @@ class ExamPapers extends BaseAdminController
return success((new ExamPapersService())->getPage($data)); return success((new ExamPapersService())->getPage($data));
} }
public function all(){
return success((new ExamPapersService())->all());
}
/** /**
* 试卷详情 * 试卷详情
* @param int $id * @param int $id

6
niucloud/app/adminapi/controller/exam_questions/ExamQuestions.php

@ -105,4 +105,10 @@ class ExamQuestions extends BaseAdminController
} }
public function to_lead_into(){
$data = $this->request->post();
return (new ExamQuestionsService())->to_lead_into($data);
}
} }

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

@ -50,7 +50,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",1], ["table_type",1],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->add($data); $id = (new LessonCourseTeachingService())->add($data);
@ -69,7 +69,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->edit($id, $data); (new LessonCourseTeachingService())->edit($id, $data);
@ -104,7 +104,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",2], ["table_type",2],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->jumpAdd($data); $id = (new LessonCourseTeachingService())->jumpAdd($data);
@ -123,7 +123,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->jumpEdit($id, $data); (new LessonCourseTeachingService())->jumpEdit($id, $data);
@ -159,7 +159,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",3], ["table_type",3],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->enAdd($data); $id = (new LessonCourseTeachingService())->enAdd($data);
@ -178,7 +178,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->enEdit($id, $data); (new LessonCourseTeachingService())->enEdit($id, $data);
@ -214,7 +214,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",4], ["table_type",4],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->basketballAdd($data); $id = (new LessonCourseTeachingService())->basketballAdd($data);
@ -233,7 +233,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->basketballEdit($id, $data); (new LessonCourseTeachingService())->basketballEdit($id, $data);
@ -269,7 +269,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",5], ["table_type",5],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->strengAdd($data); $id = (new LessonCourseTeachingService())->strengAdd($data);
@ -288,7 +288,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->strengEdit($id, $data); (new LessonCourseTeachingService())->strengEdit($id, $data);
@ -323,7 +323,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",6], ["table_type",6],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->ninjaAdd($data); $id = (new LessonCourseTeachingService())->ninjaAdd($data);
@ -342,7 +342,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->ninjaEdit($id, $data); (new LessonCourseTeachingService())->ninjaEdit($id, $data);
@ -377,7 +377,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",7], ["table_type",7],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->securityAdd($data); $id = (new LessonCourseTeachingService())->securityAdd($data);
@ -396,7 +396,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->securityEdit($id, $data); (new LessonCourseTeachingService())->securityEdit($id, $data);
@ -432,7 +432,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",8], ["table_type",8],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->physicalAdd($data); $id = (new LessonCourseTeachingService())->physicalAdd($data);
@ -451,7 +451,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->physicalEdit($id, $data); (new LessonCourseTeachingService())->physicalEdit($id, $data);
@ -486,7 +486,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",9], ["table_type",9],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->actionAdd($data); $id = (new LessonCourseTeachingService())->actionAdd($data);
@ -505,7 +505,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->actionEdit($id, $data); (new LessonCourseTeachingService())->actionEdit($id, $data);
@ -540,7 +540,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",10], ["table_type",10],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->fitnessAdd($data); $id = (new LessonCourseTeachingService())->fitnessAdd($data);
@ -559,7 +559,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->fitnessEdit($id, $data); (new LessonCourseTeachingService())->fitnessEdit($id, $data);
@ -594,7 +594,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",11], ["table_type",11],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->gamesAdd($data); $id = (new LessonCourseTeachingService())->gamesAdd($data);
@ -613,7 +613,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->gamesEdit($id, $data); (new LessonCourseTeachingService())->gamesEdit($id, $data);
@ -648,7 +648,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",12], ["table_type",12],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->relaxationAdd($data); $id = (new LessonCourseTeachingService())->relaxationAdd($data);
@ -667,7 +667,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->relaxationEdit($id, $data); (new LessonCourseTeachingService())->relaxationEdit($id, $data);
@ -694,7 +694,7 @@ class LessonCourseTeaching extends BaseAdminController
["content",""], ["content",""],
["status",0], ["status",0],
["table_type",0], ["table_type",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.add');
$id = (new LessonCourseTeachingService())->publicAdd($data); $id = (new LessonCourseTeachingService())->publicAdd($data);
@ -707,7 +707,7 @@ class LessonCourseTeaching extends BaseAdminController
["type",0], ["type",0],
["content",""], ["content",""],
["status",0], ["status",0],
["url",''] ["url",''],["exam_papers_id",""]
]); ]);
$this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit'); $this->validate($data, 'app\validate\lesson_course_teaching\LessonCourseTeaching.edit');
(new LessonCourseTeachingService())->publicEdit($id, $data); (new LessonCourseTeachingService())->publicEdit($id, $data);

1
niucloud/app/adminapi/controller/order_table/OrderTable.php

@ -108,5 +108,4 @@ class OrderTable extends BaseAdminController
public function getPersonnelAll(){ public function getPersonnelAll(){
return success(( new OrderTableService())->getPersonnelAll()); return success(( new OrderTableService())->getPersonnelAll());
} }
} }

21
niucloud/app/adminapi/controller/person_course_schedule/PersonCourseSchedule.php

@ -52,12 +52,9 @@ class PersonCourseSchedule extends BaseAdminController
*/ */
public function add(){ public function add(){
$data = $this->request->params([ $data = $this->request->params([
["person_id",0], ["resources_id",0],
["person_type",""],
["schedule_id",0], ["schedule_id",0],
["course_date","2025-05-16 17:47:53"], ["schedule_id",0]
["time_slot",""],
]); ]);
$this->validate($data, 'app\validate\person_course_schedule\PersonCourseSchedule.add'); $this->validate($data, 'app\validate\person_course_schedule\PersonCourseSchedule.add');
$id = (new PersonCourseScheduleService())->add($data); $id = (new PersonCourseScheduleService())->add($data);
@ -71,11 +68,8 @@ class PersonCourseSchedule extends BaseAdminController
*/ */
public function edit(int $id){ public function edit(int $id){
$data = $this->request->params([ $data = $this->request->params([
["person_id",0], ["resources_id",0],
["person_type",""], ["schedule_id",0]
["schedule_id",0],
["course_date","2025-05-16 17:47:53"],
["time_slot",""],
]); ]);
$this->validate($data, 'app\validate\person_course_schedule\PersonCourseSchedule.edit'); $this->validate($data, 'app\validate\person_course_schedule\PersonCourseSchedule.edit');
@ -93,5 +87,10 @@ class PersonCourseSchedule extends BaseAdminController
return success('DELETE_SUCCESS'); return success('DELETE_SUCCESS');
} }
/**
* 获取试课人员
*/
public function getTryCoursePerson($schedule_id){
return success((new PersonCourseScheduleService())->getTryCoursePerson($schedule_id));
}
} }

2
niucloud/app/adminapi/controller/sys/Role.php

@ -58,6 +58,7 @@ class Role extends BaseAdminController
['role_name', ''], ['role_name', ''],
['rules', []], ['rules', []],
['role_key',''], ['role_key',''],
['dept_id',''],
['status', RoleStatusDict::ON], ['status', RoleStatusDict::ON],
]); ]);
$this->validate($data, 'app\validate\sys\Role.add'); $this->validate($data, 'app\validate\sys\Role.add');
@ -75,6 +76,7 @@ class Role extends BaseAdminController
['role_name', ''], ['role_name', ''],
['rules', []], ['rules', []],
['role_key',''], ['role_key',''],
['dept_id',''],
['status', RoleStatusDict::ON], ['status', RoleStatusDict::ON],
]); ]);
$this->validate($data, 'app\validate\sys\Role.edit'); $this->validate($data, 'app\validate\sys\Role.edit');

28
niucloud/app/adminapi/controller/sys/System.php

@ -138,4 +138,32 @@ class System extends BaseAdminController
} }
public function jlyj_config(){
$data = $this->request->post();
return success(data: (new SystemService())->jlyj_config($data));
}
public function get_jlyj_config(){
return success(data: (new SystemService())->get_jlyj_config());
}
public function home(){
// $data = $this->request->params([
// ['start_date', date('Y-m-d', strtotime('-6 day')) ],
// ['end_date', date('Y-m-d', strtotime('+1 day'))],
// ''
// ]);
$data = $this->request->params([
['date', 'week'],
]);
return success(data: (new SystemService())->home($data));
}
} }

43
niucloud/app/adminapi/route/class_resources_rel.php

@ -0,0 +1,43 @@
<?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 -- class_resources_rel
Route::group('class_resources_rel', function () {
//班级和资源列表
Route::get('class_resources_rel', 'class_resources_rel.ClassResourcesRel/lists');
//班级和资源详情
Route::get('class_resources_rel/:id', 'class_resources_rel.ClassResourcesRel/info');
//添加班级和资源
Route::post('class_resources_rel', 'class_resources_rel.ClassResourcesRel/add');
//编辑班级和资源
Route::put('class_resources_rel/:id', 'class_resources_rel.ClassResourcesRel/edit');
//删除班级和资源
Route::delete('class_resources_rel/:id', 'class_resources_rel.ClassResourcesRel/del');
Route::get('class_grade_all','class_resources_rel.ClassResourcesRel/getClassGradeAll');
Route::get('customer_resources_all','class_resources_rel.ClassResourcesRel/getCustomerResourcesAll');
Route::get('campus_all','class_resources_rel.ClassResourcesRel/getCampusAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- class_resources_rel

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

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

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

@ -29,6 +29,11 @@ Route::group('course', function () {
//删除课程 //删除课程
Route::delete('course/:id', 'course.Course/del'); Route::delete('course/:id', 'course.Course/del');
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

@ -32,6 +32,8 @@ Route::group('course_schedule', function () {
//获取校区下的场地 //获取校区下的场地
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,
AdminCheckRole::class, AdminCheckRole::class,

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

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

1
niucloud/app/adminapi/route/exam_papers.php

@ -30,6 +30,7 @@ Route::group('exam_papers', function () {
//删除试卷 //删除试卷
Route::delete('exam_papers/:id', 'exam_papers.ExamPapers/del'); Route::delete('exam_papers/:id', 'exam_papers.ExamPapers/del');
Route::get('all', 'exam_papers.ExamPapers/all');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

1
niucloud/app/adminapi/route/exam_questions.php

@ -32,6 +32,7 @@ Route::group('exam_questions', function () {
Route::post('random_questions_list', 'exam_questions.ExamQuestions/random_questions_list'); Route::post('random_questions_list', 'exam_questions.ExamQuestions/random_questions_list');
Route::post('to_lead_into', 'exam_questions.ExamQuestions/to_lead_into');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,

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

@ -28,7 +28,8 @@ Route::group('person_course_schedule', function () {
Route::put('person_course_schedule/:id', 'person_course_schedule.PersonCourseSchedule/edit'); Route::put('person_course_schedule/:id', 'person_course_schedule.PersonCourseSchedule/edit');
//删除人员与课程安排关系 //删除人员与课程安排关系
Route::delete('person_course_schedule/:id', 'person_course_schedule.PersonCourseSchedule/del'); Route::delete('person_course_schedule/:id', 'person_course_schedule.PersonCourseSchedule/del');
//获取试课人员
Route::get('get_try_course_person/:schedule_id', 'person_course_schedule.PersonCourseSchedule/getTryCoursePerson');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

5
niucloud/app/adminapi/route/sys.php

@ -334,6 +334,11 @@ Route::group('sys', function() {
Route::post('xsyj_config', 'sys.System/xsyj_config'); Route::post('xsyj_config', 'sys.System/xsyj_config');
Route::get('get_xsyj_config', 'sys.System/get_xsyj_config'); Route::get('get_xsyj_config', 'sys.System/get_xsyj_config');
//教练业绩
Route::post('jlyj_config', 'sys.System/jlyj_config');
Route::get('get_jlyj_config', 'sys.System/get_jlyj_config');
Route::post('home', 'sys.System/home');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,

189
niucloud/app/api/controller/apiController/Chat.php

@ -0,0 +1,189 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\api\controller\apiController;
use app\Request;
use app\service\api\apiService\CampusService;
use app\service\api\apiService\ChatService;
use app\service\api\apiService\CommonService;
use core\base\BaseApiService;
/**
* 聊天控制器相关接口
* Class Personnel
* @package app\api\controller\apiController
*/
class Chat extends BaseApiService
{
//获取好友关系列表
public function getChatFriendsList(Request $request)
{
$personnel_id = $request->param('personnel_id', '');//员工人力资源表id(两个参数2选1)
$customer_resources_id = $request->param('customer_resources_id', '');//学生资源表id(两个参数2选1)
if (empty($personnel_id) && empty($customer_resources_id)) {
return fail('缺少参数');
}
$where = [
'personnel_id' => $personnel_id,
'customer_resources_id' => $customer_resources_id,
];
$res = (new ChatService())->getChatFriendsPage($where);
return success($res);
}
//获取好友关系详情
public function getChatFriendsInfo(Request $request)
{
$personnel_id = $request->param('personnel_id','');//员工人力资源表id
$customer_resources_id = $request->param('customer_resources_id','');//学生资源表id
if (empty($personnel_id) && empty($customer_resources_id)) {
return fail('缺少参数');
}
$where = [
'personnel_id' => $personnel_id,
'customer_resources_id' => $customer_resources_id,
];
$res = (new ChatService())->getChatFriendsInfo($where);
if(!$res['data']){
//创建好友关系
$add = (new ChatService())->addChatFriends($where);
if(!$add['code']){
return fail('创建好友关系失败');
}else{
$res = (new ChatService())->getChatFriendsInfo($where);
}
}
if(!$res['code']){
return fail($res['msg']);
}
return success($res['data']);
}
//创建好友关系
public function addChatFriends(Request $request)
{
$personnel_id = $request->param('personnel_id', '');
$customer_resources_id = $request->param('customer_resources_id', '');
if (empty($personnel_id) || empty($customer_resources_id)) {
return fail('缺少参数');
}
$where = [
'personnel_id' => $personnel_id,
'customer_resources_id' => $customer_resources_id,
];
//检测好友关系是否存在
$getChatFriendsInfo = (new ChatService())->getChatFriendsInfo($where);
if ($getChatFriendsInfo['data']) {
return fail('好友关系已存在');
}
$addChatFriends = (new ChatService())->addChatFriends($where);
if (!$addChatFriends['code']) {
return fail($addChatFriends['msg']);
}
return success($addChatFriends['data']);
}
//获取聊天记录
public function getChatMessagesList(Request $request)
{
$friend_id = $request->param('friend_id', '');//关联chat_friends表id
if (empty($friend_id)) {
return fail('发送者类型不正确');
}
$where = [
'friend_id' => $friend_id,
];
$res = (new ChatService())->getChatMessagesList($where);
return success($res);
}
//发送聊天内容
public function sendChatMessages(Request $request)
{
$from_type = $request->param('from_type', '');//发送者类型|personnel=员工,customer=学生(客户)
$from_id = $request->param('from_id', '');//发送者ID
$to_id = $request->param('to_id', '');//接收者ID
$friend_id = $request->param('friend_id', '');//关联chat_friends表id
$message_type = $request->param('message_type', '');//消息类型|text=文本,img=图片
$content = $request->param('content', '');//文本内容(JSON 格式扩展字段),文本类型=纯文字,图片类型=绝对路径
if (!in_array($message_type, ['text', 'img'])) {
return fail('消息类型不正确');
}
if (!in_array($from_type, ['personnel', 'customer'])) {
return fail('发送者类型不正确');
}
if (empty($from_id) || empty($to_id) || empty($friend_id) || empty($content)) {
return fail('缺少参数');
}
$data = [
'from_type' => $from_type, // 发送者类型:personnel=员工,customer=学生(客户)
'from_id' => $from_id, // 发送者ID(员工/学生),根据from_type判断
'to_id' => $to_id, // 接收者ID(员工/学生),根据from_type判断
'friend_id' => $friend_id, // 关联chat_friends表id
'message_type' => $message_type, // 消息类型:text=文本,img=图片
'content' => $content // 文本内容(JSON格式扩展字段)
];
$res = (new ChatService())->sendChatMessages($data);
if(!$res['code']){
return fail($res['msg']);
}
return success($res['data']);
}
//修改未读消息数量
public function editUnreadCount(Request $request){
$from_type = $request->param('from_type', '');//发送者类型|personnel=员工,customer=学生(客户)
$from_id = $request->param('from_id', '');//发送者ID
$friend_id = $request->param('friend_id', '');//关联chat_friends表id
if (!in_array($from_type, ['personnel', 'customer'])) {
return fail('发送者类型不正确');
}
if (empty($from_id) || empty($friend_id)) {
return fail('缺少参数');
}
$res = (new ChatService())->editUnreadCount($from_type,$friend_id);
// if(!$res){
// return fail('修改失败');
// }
return success($res);
}
}

12
niucloud/app/api/route/route.php

@ -240,6 +240,18 @@ Route::group(function () {
//员工考勤-编辑(员工打卡/请假/签退) //员工考勤-编辑(员工打卡/请假/签退)
Route::post('attendance/edit', 'apiController.Attendance/edit'); Route::post('attendance/edit', 'apiController.Attendance/edit');
//员工端-用户聊天-好友关系列表
Route::get('chat/getChatFriendsList', 'apiController.Chat/getChatFriendsList');
//员工端-用户聊天-创建好友关系
Route::post('chat/addChatFriends', 'apiController.Chat/addChatFriends');
//员工端-用户聊天-好友关系详情
Route::get('chat/getChatFriendsInfo', 'apiController.Chat/getChatFriendsInfo');
//员工端-用户聊天-获取聊天记录
Route::get('chat/getChatMessagesList', 'apiController.Chat/getChatMessagesList');
//员工端-用户聊天-发送聊天内容
Route::post('chat/sendChatMessages', 'apiController.Chat/sendChatMessages');
//员工端-用户聊天-修改未读消息数量
Route::post('chat/editUnreadCount', 'apiController.Chat/editUnreadCount');

7
niucloud/app/common.php

@ -1145,3 +1145,10 @@ function decryptWechatPayNotify($ciphertext, $nonce, $associatedData, $key)
$associatedData $associatedData
); );
} }
/**
* 判断是否为手机号
*/
function isPhone($mobile)
{
return preg_match('/^1[3456789]\d{9}$/', $mobile);
}

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 = 'delete_time';
/**
* 定义软删除字段的默认值.
* @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 = 'delete_time';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
}

68
niucloud/app/model/class_resources_rel/ClassResourcesRel.php

@ -0,0 +1,68 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\model\class_resources_rel;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
use app\model\class_grade\ClassGrade;
use app\model\customer_resources\CustomerResources;
use app\model\campus\Campus;
/**
* 班级和资源模型
* Class ClassResourcesRel
* @package app\model\class_resources_rel
*/
class ClassResourcesRel extends BaseModel
{
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'class_resources_rel';
public function classGrade(){
return $this->hasOne(ClassGrade::class, 'id', 'class_id')->joinType('left')->withField('class_name,id')->bind(['class_id_name'=>'class_name']);
}
public function customerResources(){
return $this->hasOne(CustomerResources::class, 'id', 'resource_id')->joinType('left')->withField('name,id')->bind(['resource_id_name'=>'name']);
}
public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']);
}
}

50
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;
@ -50,6 +53,53 @@ class CourseSchedule extends BaseModel
*/ */
protected $defaultSoftDelete = 0; protected $defaultSoftDelete = 0;
protected $json = ['participants', 'student_ids'];
protected $jsonAssoc = true;
/**
* 搜索器:场地校区
* @param $value
* @param $data
*/
public function searchCampusIdAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_id", $value);
}
}
/**
* 搜索器:场地ID
* @param $value
* @param $data
*/
public function searchVenueIdAttr($query, $value, $data)
{
if ($value) {
$query->where("venue_id", $value);
}
}
public function searchCourseDateAttr($query, $value, $data)
{
if ($value) {
$query->where("course_date", $value);
}
}
public function venue()
{
return $this->hasOne(Venue::class, 'id', 'venue_id');
}
public function coach()
{
return $this->hasOne(Personnel::class, 'id', 'coach_id');
}
public function course()
{
return $this->hasOne(Course::class, 'id', 'course_id');
}
} }

5
niucloud/app/model/customer_resources/CustomerResources.php

@ -12,6 +12,7 @@
namespace app\model\customer_resources; namespace app\model\customer_resources;
use app\model\dict\Dict; use app\model\dict\Dict;
use app\model\order_table\OrderTable;
use app\model\resource_sharing\ResourceSharing; use app\model\resource_sharing\ResourceSharing;
use app\model\six_speed\SixSpeed; use app\model\six_speed\SixSpeed;
use core\base\BaseModel; use core\base\BaseModel;
@ -89,6 +90,10 @@ class CustomerResources extends BaseModel
'status' => '客户状态', 'status' => '客户状态',
]; ];
public function orderTable()
{
return $this->hasMany(OrderTable::class, 'resource_id', 'id');
}
/** /**
* 搜索器:客户资源姓名 * 搜索器:客户资源姓名
* @param $value * @param $value

31
niucloud/app/model/person_course_schedule/PersonCourseSchedule.php

@ -11,6 +11,9 @@
namespace app\model\person_course_schedule; namespace app\model\person_course_schedule;
use app\model\customer_resources\CustomerResources;
use app\model\personnel\Personnel;
use app\model\student\Student;
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;
@ -23,9 +26,6 @@ use think\model\relation\HasOne;
*/ */
class PersonCourseSchedule extends BaseModel class PersonCourseSchedule extends BaseModel
{ {
use SoftDelete;
/** /**
* 数据表主键 * 数据表主键
* @var string * @var string
@ -38,18 +38,6 @@ class PersonCourseSchedule extends BaseModel
*/ */
protected $name = 'person_course_schedule'; protected $name = 'person_course_schedule';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'deleted_at';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/** /**
* 搜索器:人员与课程安排关系关系编号 * 搜索器:人员与课程安排关系关系编号
* @param $value * @param $value
@ -123,8 +111,19 @@ class PersonCourseSchedule extends BaseModel
} }
public function person()
{
return $this->hasOne(Personnel::class, 'id', 'person_id');
}
public function student()
{
return $this->hasOne(Student::class, 'id', 'student_id');
}
public function resources()
{
return $this->hasOne(CustomerResources::class, 'id', 'resources_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');
} }
} }

36
niucloud/app/service/admin/campus_person_role/CampusPersonRoleService.php

@ -38,12 +38,36 @@ class CampusPersonRoleService extends BaseAdminService
* @param array $where * @param array $where
* @return array * @return array
*/ */
public function getPage(array $where = []) public function getPage(array $data = [])
{ {
$field = 'id,campus_id,person_id,role_id,dept_id,created_at,updated_at,deleted_at'; $field = 'a.*,b.phone';
$order = 'id desc'; $order = 'a.id desc';
$where = [];
if($data['campus_id']){
$where[] = ['a.campus_id','=',$data['campus_id']];
}
if($data['role_id']){
$where[] = ['a.role_id','=',$data['role_id']];
}
if($data['dept_id']){
$where[] = ['a.dept_id','=',$data['dept_id']];
}
if($data['person_name']){
$where[] = ['b.name','like','%'.$data['person_name'].'%'];
}
$search_model = $this->model
->alias("a")
->join(['school_personnel' => 'b'],'a.person_id = b.id','left')
->where($where)
->with(['campus','personnel','sysRole','departments'])
->field($field)->order($order);
$search_model = $this->model->withSearch(["id","campus_id","person_id","role_id","dept_id"], $where)->with(['campus','personnel','sysRole','departments'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -109,9 +133,9 @@ class CampusPersonRoleService extends BaseAdminService
return $personnelModel->select()->toArray(); return $personnelModel->select()->toArray();
} }
public function getSysRoleAll(){ public function getSysRoleAll($data){
$sysRoleModel = new SysRole(); $sysRoleModel = new SysRole();
return $sysRoleModel->select()->toArray(); return $sysRoleModel->where(['dept_id' => $data['dept_id']])->select()->toArray();
} }
public function getDepartmentsAll(){ public function getDepartmentsAll(){

117
niucloud/app/service/admin/class_resources_rel/ClassResourcesRelService.php

@ -0,0 +1,117 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\admin\class_resources_rel;
use app\model\class_resources_rel\ClassResourcesRel;
use app\model\class_grade\ClassGrade;
use app\model\customer_resources\CustomerResources;
use app\model\campus\Campus;
use core\base\BaseAdminService;
/**
* 班级和资源服务层
* Class ClassResourcesRelService
* @package app\service\admin\class_resources_rel
*/
class ClassResourcesRelService extends BaseAdminService
{
public function __construct()
{
parent::__construct();
$this->model = new ClassResourcesRel();
}
/**
* 获取班级和资源列表
* @param array $where
* @return array
*/
public function getPage(array $where = [])
{
$field = 'id,class_id,resource_id,campus_id,source_id,source_type,join_time,out_time,status,create_time,update_time';
$order = 'id desc';
$search_model = $this->model->withSearch([], $where)->with(['classGrade','customerResources','campus'])->field($field)->order($order);
$list = $this->pageQuery($search_model);
return $list;
}
/**
* 获取班级和资源信息
* @param int $id
* @return array
*/
public function getInfo(int $id)
{
$field = 'id,class_id,resource_id,campus_id,source_id,source_type,join_time,out_time,status,create_time,update_time';
$info = $this->model->field($field)->where([['id', "=", $id]])->with(['classGrade','customerResources','campus'])->findOrEmpty()->toArray();
return $info;
}
/**
* 添加班级和资源
* @param array $data
* @return mixed
*/
public function add(array $data)
{
$res = $this->model->create($data);
return $res->id;
}
/**
* 班级和资源编辑
* @param int $id
* @param array $data
* @return bool
*/
public function edit(int $id, array $data)
{
$this->model->where([['id', '=', $id]])->update($data);
return true;
}
/**
* 删除班级和资源
* @param int $id
* @return bool
*/
public function del(int $id)
{
$model = $this->model->where([['id', '=', $id]])->find();
$res = $model->delete();
return $res;
}
public function getClassGradeAll(){
$classGradeModel = new ClassGrade();
return $classGradeModel->select()->toArray();
}
public function getCustomerResourcesAll(){
$customerResourcesModel = new CustomerResources();
return $customerResourcesModel->select()->toArray();
}
public function getCampusAll(){
$campusModel = new Campus();
return $campusModel->select()->toArray();
}
}

31
niucloud/app/service/admin/communication_records/CommunicationRecordsService.php

@ -36,12 +36,35 @@ class CommunicationRecordsService extends BaseAdminService
* @param array $where * @param array $where
* @return array * @return array
*/ */
public function getPage(array $where = []) public function getPage(array $data = [])
{ {
$field = 'id,staff_id,resource_id,resource_type,communication_type,communication_result,communication_time,remarks,tag,business_id,created_at,updated_at'; $field = 'a.*';
$order = 'id desc'; $order = 'a.id desc';
$where = [];
if($data['name']){
$where[] = ['b.name','like','%'.$data['name'].'%'];
}
if($data['phone_number']){
$where[] = ['b.phone_number','like','%'.$data['phone_number'].'%'];
}
if($data['campus']){
$where[] = ['c.name','like','%'.$data['campus'].'%'];
}
if($data['communication_type']){
$where[] = ['a.communication_type','=',$data['communication_type']];
}
$search_model = $this->model->withSearch(["resource_id","communication_type"], $where)->with(['customerResources'])->field($field)->order($order); $search_model = $this->model
->alias("a")
->join(['school_customer_resources' => 'b'],'a.resource_id = b.id','left')
->join(['school_campus' => 'c'],'b.campus = c.id','left')
->where($where)
->with(['customerResources'])
->field($field)
->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }

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

@ -11,6 +11,7 @@
namespace app\service\admin\course; namespace app\service\admin\course;
use app\model\contract\Contract;
use app\model\course\Course; use app\model\course\Course;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -36,7 +37,7 @@ class CourseService extends BaseAdminService
*/ */
public function getPage(array $where = []) public function getPage(array $where = [])
{ {
$field = 'id,course_name,course_type,duration,session_count,single_session_count,price,internal_reminder,customer_reminder,remarks,created_at,updated_at,deleted_at'; $field = 'id,course_name,course_type,duration,session_count,contract_id,single_session_count,price,internal_reminder,customer_reminder,remarks,created_at,updated_at,deleted_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","course_name","course_type","duration","session_count","single_session_count","price","internal_reminder","customer_reminder","remarks"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["id","course_name","course_type","duration","session_count","single_session_count","price","internal_reminder","customer_reminder","remarks"], $where)->field($field)->order($order);
@ -44,6 +45,12 @@ class CourseService extends BaseAdminService
return $list; return $list;
} }
public function contract_all(){
$contract = new Contract();
$list = $contract->select()->toArray();
return $list;
}
/** /**
* 获取课程信息 * 获取课程信息
* @param int $id * @param int $id
@ -51,7 +58,7 @@ class CourseService extends BaseAdminService
*/ */
public function getInfo(int $id) public function getInfo(int $id)
{ {
$field = 'id,course_name,course_type,duration,session_count,single_session_count,price,internal_reminder,customer_reminder,remarks,created_at,updated_at,deleted_at'; $field = 'id,course_name,course_type,duration,session_count,contract_id,single_session_count,price,internal_reminder,customer_reminder,remarks,created_at,updated_at,deleted_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray();
return $info; return $info;
@ -94,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();
}
} }

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

@ -13,6 +13,9 @@ 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\model\personnel\Personnel;
use app\model\venue\Venue;
use app\service\admin\venue\VenueService;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -36,10 +39,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,auto_schedule';
$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 +57,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,auto_schedule';
$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 +70,28 @@ class CourseScheduleService extends BaseAdminService
*/ */
public function add(array $data) public function add(array $data)
{ {
$res = $this->model->create($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'],
'auto_schedule' => $data['auto_schedule'],
'available_capacity' => (new Venue())->where('id', $data['venue_id'])->value('capacity')
];
$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; return $res->id;
} }
/** /**
@ -77,8 +102,27 @@ class CourseScheduleService extends BaseAdminService
*/ */
public function edit(int $id, array $data) public function edit(int $id, array $data)
{ {
$create = [
$this->model->where([['id', '=', $id]])->update($data); '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'],
'auto_schedule' => $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();
if ($status) {
throw new \Exception('该时间段已有课程安排');
}
$this->model->where([['id', '=', $id]])->update($create);
return true; return true;
} }
@ -127,33 +171,33 @@ class CourseScheduleService extends BaseAdminService
$person_schedules = []; $person_schedules = [];
if (!empty($schedule_ids)) { if (!empty($schedule_ids)) {
$person_schedules = (new PersonCourseSchedule())->where([ $person_schedules = (new PersonCourseSchedule())->with(['resources', 'person', 'student'])->where([
['schedule_id', 'in', $schedule_ids] ['schedule_id', 'in', $schedule_ids]
])->select()->toArray(); ])
->select()
->toArray();
} }
// 教师
$coach_ids = array_column($schedules, 'coach_id');
$coaches = (new Personnel())->where('id', 'in', $coach_ids)->select()->toArray();
// 组织数据结构 // 组织数据结构
$days = []; $days = [];
$weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; $weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
// 获取所有时间段和教室 // 获取所有时间段和教室
$time_slots = []; $time_slots = [];
$classrooms = []; $classrooms = (new VenueService())->getVenueAll($campus_id);
// 如果没有数据,设置默认的时间段和教室 // 如果没有数据,设置默认的时间段和教室
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']; $time_slots = (new VenueService())->getVenueTime($classrooms);
$classrooms = [1, 2]; // 默认教室ID
} 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代表教室
if (!in_array($schedule['venue_id'], $classrooms)) {
$classrooms[] = $schedule['venue_id'];
}
} }
} }
@ -162,48 +206,58 @@ class CourseScheduleService extends BaseAdminService
$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 : [];
if (!empty($day_schedules)) {
foreach ($day_schedules as $schedule) {
if (!in_array($schedule['venue_id'], $day_classrooms)) {
$day_classrooms[] = $schedule['venue_id'];
}
}
}
// 构建每个时间段的数据 // 构建每个时间段的数据
$day_time_slots = []; $day_time_slots = [];
foreach ($time_slots as $time_slot) { foreach ($time_slots as $time_slot) {
$slot_data = [ $slot_data = [
'timeRange' => $time_slot, 'timeRange' => $time_slot,
'color' => '#' . dechex(rand(0x000000, 0xFFFFFF)), // 随机颜色 'color' => '#FFFFFF', // 默认白色背景
'textColor' => '#000000', // 默认黑色文字
]; ];
// 查找该时间段的课程 // 查找该时间段的课程
$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'); $teacher = array_filter($coaches, function ($coach) use ($schedule) {
return $coach['id'] == $schedule['coach_id'];
});
$venues = array_filter($classrooms, function ($venue) use ($schedule) {
return $venue['id'] == $schedule['venue_id'];
});
$available_capacity = $schedule['available_capacity'] - count($students);
if ($available_capacity > 0) {
$slot_data['backgroundColor'] = '#079307'; // 绿色背景
$slot_data['color'] = '#FFFFFF'; // 白色文字
} elseif ($available_capacity == 0) {
$slot_data['backgroundColor'] = '#FF0000'; // 红色背景
$slot_data['color'] = '#FFFFFF'; // 白色文字
} else {
$slot_data['backgroundColor'] = '#FFFFFF'; // 白色背景
$slot_data['color'] = '#000000'; // 黑色文字
}
$slot_data['course'] = [ $slot_data['course'] = [
'teacher' => $schedule['coach_id'] ?? '', 'teacher' => is_array($teacher) ? array_values($teacher) : [],
'students' => $student_names, 'students' => is_array($students) ? array_values($students) : [],
'classroom' => $schedule['venue_id'], 'classroom' => is_array($venues) ? array_values($venues) : [],
'hasnumber' => $schedule['available_capacity'] ?? 0, 'hasnumber' => $available_capacity,
'schedule' => $schedule,
]; ];
} }
@ -213,7 +267,7 @@ class CourseScheduleService extends BaseAdminService
$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' => $classrooms,
]; ];
$current_date = date('Y-m-d', strtotime($current_date . ' +1 day')); $current_date = date('Y-m-d', strtotime($current_date . ' +1 day'));
@ -224,56 +278,14 @@ class CourseScheduleService extends BaseAdminService
} }
/** /**
* 自动创建课程安排记录 * @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 (new Venue())->where('availability_status', 1)->where('campus_id', $id)->select();
} }
} }

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

@ -11,8 +11,11 @@
namespace app\service\admin\customer_resources; namespace app\service\admin\customer_resources;
use app\model\campus_person_role\CampusPersonRole;
use app\model\course_schedule\CourseSchedule;
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\order_table\OrderTable;
use app\model\personnel\Personnel; use app\model\personnel\Personnel;
use app\model\campus\Campus; use app\model\campus\Campus;
@ -47,44 +50,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 +109,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 +135,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,7 +146,7 @@ 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,
@ -150,7 +154,7 @@ class CustomerResourcesService extends BaseAdminService
'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 +174,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,10 +222,10 @@ 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'],
@ -234,13 +238,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,11 +259,11 @@ 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,
@ -273,7 +276,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 +297,29 @@ class CustomerResourcesService extends BaseAdminService
} }
public function getPersonnelAll($data){ public function getPersonnelAll($data)
{
$personnelModel = new Personnel(); $personnelModel = new Personnel();
$where = []; $where = [];
if($data['role_id']){ if ($data['role_id']) {
$where[] = ['b.role_id','=',$data['role_id']]; $where[] = ['b.role_id', '=', $data['role_id']];
} }
return $personnelModel return $personnelModel
->alias("a") ->alias("a")
->join(['school_campus_person_role' => 'b'],'a.id = b.person_id','left') ->join(['school_campus_person_role' => 'b'], 'a.id = b.person_id', 'left')
->field("a.*") ->field("a.*")
->where($where)->select()->toArray(); ->where($where)->select()->toArray();
} }
public function getCampusAll(){ public function getCampusAll()
{
$campusModel = new Campus(); $campusModel = new Campus();
return $campusModel->select()->toArray(); 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 +328,84 @@ class CustomerResourcesService extends BaseAdminService
return "分配成功"; return "分配成功";
} }
public function personnelAllByname($name)
{
$query = $this->model->whereNotExists(function ($query) {
$query->name('order_table') // 订单表名
->whereRaw('school_order_table.resource_id = school_customer_resources.id');
});
//如果$name是手机号,则按手机号查询
if (isPhone($name)) {
$query = $query->where('phone_number', $name);
} else {
$query = $query->where('name', 'like', '%' . $name . '%');
}
return $query->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();
}
/**
* 获取课程下的资源和学员
* @param $course_id
* @return mixed
*/
public function getResourceByCourse($schedule_id)
{
$schedule = CourseSchedule::find($schedule_id);
$course_id = $schedule->course_id;
$all = (new OrderTable())
->alias('a')
->join(['school_customer_resources' => 'b'], 'a.resource_id = b.id')
->join(['school_student' => 'c'], 'a.resource_id = c.user_id')
->field('b.name, b.phone_number, b.id, c.name as student_name, c.age, c.id as student_id, a.id as order_id')
->where([
['a.course_id', '=', $course_id],
['a.order_status', '=', 'paid']
])
->group('a.id')
->select()
->toArray();
// 假设 $selectedCode 是一个数组,包含已经加入课程的人员 ID
$selectedCode = $schedule->participants;
// 遍历 $all,为每个元素添加 checked 属性
foreach ($all as &$item) {
$item['checked'] = in_array($item['id'], $selectedCode ? $selectedCode : []);
}
return $all;
}
} }

11
niucloud/app/service/admin/exam_papers/ExamPapersService.php

@ -44,6 +44,17 @@ class ExamPapersService extends BaseAdminService
return $list; return $list;
} }
public function all()
{
$field = 'id,title';
$order = 'id desc';
$list = $this->model->field($field)->order($order)->select()->toArray();
return $list;
}
/** /**
* 获取试卷信息 * 获取试卷信息
* @param int $id * @param int $id

79
niucloud/app/service/admin/exam_questions/ExamQuestionsService.php

@ -14,6 +14,7 @@ namespace app\service\admin\exam_questions;
use app\model\exam_questions\ExamQuestions; use app\model\exam_questions\ExamQuestions;
use core\base\BaseAdminService; use core\base\BaseAdminService;
use PhpOffice\PhpSpreadsheet\IOFactory;
/** /**
@ -120,5 +121,83 @@ class ExamQuestionsService extends BaseAdminService
return success("随机成功", ['questions_ids' => implode(',', $questions_ids)]); return success("随机成功", ['questions_ids' => implode(',', $questions_ids)]);
} }
public function to_lead_into(array $data)
{
$filePath = public_path() . $data['url'];
try {
$spreadsheet = IOFactory::load($filePath);
$worksheet = $spreadsheet->getActiveSheet();
$highestRow = $worksheet->getHighestRow();
$list = [];
// 遍历每一行
foreach ($worksheet->getRowIterator() as $rowIndex => $row) {
if ($rowIndex == 1) {
// 跳过第一行(表头)和最后一行
continue;
}
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
$data = [];
foreach ($cellIterator as $cell) {
$value = $cell->getValue(); // 获取每个单元格的值
if ($value instanceof \PhpOffice\PhpSpreadsheet\RichText\RichText) {
$value = $value->getPlainText();
}
$data[] = $value;
}
$list[] = $data;
}
$inserAll = [];
$question_type_arr = ['单选' => 'single_choice','多选'=>'multiple_choice','判断' => 'true_false'];
foreach ($list as $key => $item) {
$option_json = [
[
'option_content_type' => $item[4],
'option_content' => $item[5],
'option' => 'A',
'correct_answer' => str_contains($item[12], 'A')
],
[
'option_content_type' => $item[6],
'option_content' => $item[7],
'option' => 'B',
'correct_answer' => str_contains($item[12], 'B')
],
[
'option_content_type' => $item[8],
'option_content' => $item[9],
'option' => 'C',
'correct_answer' => str_contains($item[12], 'C')
],
[
'option_content_type' => $item[10],
'option_content' => $item[11],
'option' => 'D',
'correct_answer' => str_contains($item[12], 'D')
]
];
$inserAll[] = [
'title' => $item[0],
'question_type' =>$question_type_arr[$item[1]],
'question_content_type' => $item[2],
'question_content' => $item[3],
'option_json' => json_encode($option_json, JSON_UNESCAPED_UNICODE),
'correct_answer' => $item[12]
];
}
$this->model->insertAll($inserAll);
return success("导入成功");
} catch (\PhpOffice\PhpSpreadsheet\Reader\Exception $e) {
return fail($e->getMessage());
}
}
} }

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

@ -38,7 +38,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function getPage(array $where = []) public function getPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
@ -85,7 +85,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function jumpPetPage(array $where = []) public function jumpPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -129,7 +129,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function enPetPage(array $where = []) public function enPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -174,7 +174,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function basketballPetPage(array $where = []) public function basketballPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -218,7 +218,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function strengPetPage(array $where = []) public function strengPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -262,7 +262,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function ninjaPetPage(array $where = []) public function ninjaPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -306,7 +306,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function securityPetPage(array $where = []) public function securityPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -350,7 +350,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function physicalPetPage(array $where = []) public function physicalPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -398,7 +398,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function actionPetPage(array $where = []) public function actionPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -443,7 +443,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function gamesPetPage(array $where = []) public function gamesPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -489,7 +489,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function fitnessPetPage(array $where = []) public function fitnessPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -534,7 +534,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function relaxationPetPage(array $where = []) public function relaxationPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -574,7 +574,7 @@ class LessonCourseTeachingService extends BaseAdminService
public function publicPetPage(array $where = []) public function publicPetPage(array $where = [])
{ {
$field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission'; $field = 'id,title,image,type,content,status,create_time,update_time,delete_time,table_type,user_permission,exam_papers_id';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order); $search_model = $this->model->withSearch(["title","status","create_time","update_time","table_type"], $where)->with(['personnelData'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
@ -611,7 +611,7 @@ class LessonCourseTeachingService extends BaseAdminService
*/ */
public function getInfo(int $id) public function getInfo(int $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';
$info = $this->model->field($field)->where([['id', "=", $id]])->with(['personnelData'])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['personnelData'])->findOrEmpty()->toArray();
$info['status'] = strval($info['status']); $info['status'] = strval($info['status']);
$info['type'] = strval($info['type']); $info['type'] = strval($info['type']);

108
niucloud/app/service/admin/person_course_schedule/PersonCourseScheduleService.php

@ -11,8 +11,12 @@
namespace app\service\admin\person_course_schedule; namespace app\service\admin\person_course_schedule;
use app\model\course_schedule\CourseSchedule;
use app\model\customer_resources\CustomerResources;
use app\model\person_course_schedule\PersonCourseSchedule; use app\model\person_course_schedule\PersonCourseSchedule;
use app\model\student\Student;
use app\model\venue\Venue;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -39,7 +43,7 @@ class PersonCourseScheduleService extends BaseAdminService
$field = 'id,person_id,person_type,schedule_id,course_date,time_slot,created_at,updated_at,deleted_at'; $field = 'id,person_id,person_type,schedule_id,course_date,time_slot,created_at,updated_at,deleted_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","person_id","person_type","schedule_id","course_date","time_slot"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["id", "person_id", "person_type", "schedule_id", "course_date", "time_slot"], $where)->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -64,9 +68,82 @@ class PersonCourseScheduleService extends BaseAdminService
*/ */
public function add(array $data) public function add(array $data)
{ {
$res = $this->model->create($data); // 获取所有要新增的学生
return $res->id; $students = (new Student())->whereIn('user_id', $data['resources_id'])->column('user_id', 'id');
// 新增的 participant 列表
$newParticipants = $data['resources_id'];
// 获取当前课程安排
$schedule = (new CourseSchedule())->find($data['schedule_id']);
if (!$schedule) {
return $this->error('课程安排不存在');
}
$capacity = (new Venue())->where('id', $schedule->venue_id)->value('capacity');
// 原来的 participant 列表
$oldParticipants = $schedule->participants ?? [];
// 计算差集:哪些是被删除的(需要从预约记录中删除)
$deletedParticipants = array_diff($oldParticipants, $newParticipants);
// 计算可用容量
$addedCount = count($newParticipants) - count($oldParticipants);
$deletedCount = count($deletedParticipants);
$available_capacity = $capacity + $deletedCount - $addedCount;
if ($available_capacity < 0) {
return $this->error('当前课程安排已满');
}
try {
$this->model->startTrans();
// 1. 更新课程安排信息
(new CourseSchedule())->where('id', $data['schedule_id'])->update([
'participants' => $newParticipants,
'student_ids' => array_keys($students),
'available_capacity' => $available_capacity
]);
// 2. 删除旧的预约记录(针对被移除的人)
if (!empty($deletedParticipants)) {
$this->model->whereIn('resources_id', $deletedParticipants)
->where('course_date', $schedule->course_date)
->where('time_slot', $schedule->time_slot)
->delete();
}
// 3. 删除原有的,再批量插入新的预约记录(避免重复)
foreach ($newParticipants as $participant) {
$student_id = array_flip($students)[$participant] ?? 0;
// 先删除已存在的记录(防止重复)
$this->model->where([
['resources_id', '=', $participant],
['course_date', '=', $schedule->course_date],
['time_slot', '=', $schedule->time_slot]
])->delete();
// 插入新的记录
$this->model->create([
'resources_id' => $participant,
'person_id' => $this->uid,
'schedule_id' => $data['schedule_id'],
'student_id' => $student_id,
'person_type' => $student_id ? 'student' : 'customer_resource',
'course_date' => $schedule->course_date,
'time_slot' => $schedule->time_slot
]);
}
$this->model->commit();
} catch (\Exception $e) {
$this->model->rollback();
return $this->error('操作失败: ' . $e->getMessage());
}
return true;
} }
/** /**
@ -94,6 +171,27 @@ class PersonCourseScheduleService extends BaseAdminService
return $res; return $res;
} }
public function getTryCoursePerson($schedule_id)
{
$list = $this->model->where('person_type', 'customer_resource')
->where('schedule_id', $schedule_id)
->select();
$resources = (new CustomerResources())->whereIn('id', $list->column('resources_id'))->select()->toArray();
$data = [];
foreach ($resources as $key => $value) {
// 构建符合需求的对象
$data[] = [
"name" => $value['name'],
"phone_number" => $value['phone_number'],
"id" => $value['id'],
"student_name" => null,
"age" => $value['age'] ?? null,
"student_id" => null,
"order_id" => null,
"checked" => true
];
}
return $data;
}
} }

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

@ -11,6 +11,7 @@
namespace app\service\admin\student; namespace app\service\admin\student;
use app\model\class_resources_rel\ClassResourcesRel;
use app\model\student\Student; use app\model\student\Student;
use app\model\customer_resources\CustomerResources; use app\model\customer_resources\CustomerResources;
use app\model\campus\Campus; use app\model\campus\Campus;
@ -42,7 +43,7 @@ class StudentService extends BaseAdminService
public function getPage(array $where = []) public function getPage(array $where = [])
{ {
$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 desc';
$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) {
@ -60,6 +61,17 @@ 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());
$info['getInfoRel'] = $this->getInfoRel($info['user_id']);
return $info;
}
public function getInfoRel(int $resource_id)
{
$rel = new ClassResourcesRel();
$field = 'id,class_id,resource_id,campus_id,source_id,source_type,join_time,out_time,status,create_time,update_time';
$info = $rel->field($field)->where([['resource_id', "=", $resource_id]])->with(['classGrade','customerResources','campus'])->findOrEmpty()->toArray();
return $info; return $info;
} }

14
niucloud/app/service/admin/sys/RoleService.php

@ -44,10 +44,16 @@ class RoleService extends BaseAdminService
{ {
$where = []; $where = [];
if(isset($data['role_name']) && $data['role_name'] !== '') { if(isset($data['role_name']) && $data['role_name'] !== '') {
$where[] = ['role_name', 'like', "%".$this->model->handelSpecialCharacter($data['role_name'])."%"]; $where[] = ['a.role_name', 'like', "%".$this->model->handelSpecialCharacter($data['role_name'])."%"];
} }
$field = 'role_id,role_name,role_key,status,create_time'; $field = 'a.role_id,a.role_name,a.role_key,a.status,a.create_time,b.department_name';
$search_model = $this->model->where($where)->field($field)->order('create_time desc')->append(['status_name']); $search_model = $this->model
->alias("a")
->join(['school_departments' => 'b'],'a.dept_id = b.id','left')
->where($where)
->field($field)
->order('a.create_time desc')
->append(['status_name']);
return $this->pageQuery($search_model); return $this->pageQuery($search_model);
} }
/** /**

77
niucloud/app/service/admin/sys/SystemService.php

@ -11,7 +11,12 @@
namespace app\service\admin\sys; namespace app\service\admin\sys;
use addon\shop\app\model\ShopStat;
use app\job\sys\CheckJob; use app\job\sys\CheckJob;
use app\model\campus\Campus;
use app\model\customer_resources\CustomerResources;
use app\model\personnel\Personnel;
use app\model\student\Student;
use app\model\sys\SysConfig; use app\model\sys\SysConfig;
use app\service\core\sys\CoreSysConfigService; use app\service\core\sys\CoreSysConfigService;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -208,4 +213,76 @@ class SystemService extends BaseAdminService
return $data; return $data;
} }
public function jlyj_config(array $data){
$config = new SysConfig();
$config->where(['config_key' => 'JLYJ'])->update([
'value' => json_encode($data)
]);
return true;
}
public function get_jlyj_config(){
$config = new SysConfig();
$data = $config->where(['config_key' => 'JLYJ'])->value("value");
return $data;
}
public function home(array $arr){
$campus = new Campus();
$personnel = new Personnel();
$customerResources = new CustomerResources();
$student = new Student();
$data = [];
$data['campus_count'] = $campus->count();
$data['personnel_count'] = $personnel->count();
$data['customerResources_count'] = $customerResources->count();
$data['customerResources_day_count'] = $customerResources->whereTime('created_at', 'yesterday')->count();
$data['student_count'] = $student->count();
$data['student_day_count'] = $student->whereTime('created_at', 'yesterday')->count();
if ($arr['date'] == 'week') {
$start_date = date('Y-m-d', strtotime('-6 days'));
$end_date = date('Y-m-d',strtotime('+1 days'));
} elseif ($arr['date'] == 'month') {
$start_date = date('Y-m-01');
$end_date = date('Y-m-d');
} elseif ($arr['date'] == 'year') {
$start_date = date('Y-01-01');
$end_date = date('Y-m-d');
} else {
$start_date = date('Y-m-d', strtotime('-6 days'));
$end_date = date('Y-m-d',strtotime('+1 days'));
}
$stat_data = $customerResources
->where([ ['create_date', '>=', $start_date], ['create_date', '<=', $end_date] ])
->field('create_date, COUNT(*) as count')
->group('create_date')
->select()->toArray();
$stat_data = !empty($stat_data) ? array_column($stat_data, 'count', 'create_date') : [];
$day = ceil((strtotime($end_date) - strtotime($start_date)) / 86400);
$value = [];
$time = [];
for ($i = 0; $i < $day; $i++) {
$date = date('Y-m-d', strtotime($start_date) + $i * 86400);
$time[] = $date;
$value[] = isset($stat_data[ $date ]) ? $stat_data[ $date ] : 0;
}
$data['customer'][ 'value' ] = $value;
$data['customer'][ 'time' ] = $time;
return $data;
}
} }

89
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) {
if ($venue_info['time_range_type'] === 'fixed') { // 记录错误日志
$venue_info['time_range'] = json_decode($venue_info['fixed_time_ranges'], true); \think\facade\Log::error('解析固定时间段失败: ' . $e->getMessage());
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点结束 break;
if ($venue_info['time_range_type'] === 'all') {
for ($i = 8; $i <= 22; $i++) { case 'all':
$venue_info['time_range'][] = $i; // 全天可用,但中午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;
return $venue_info; default:
// 不支持的类型返回空数组
break;
}
}
// $timeSlots去重
return array_unique($timeSlots);
} }
} }

216
niucloud/app/service/api/apiService/ChatService.php

@ -0,0 +1,216 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\api\apiService;
use app\model\campus\Campus;
use app\model\campus_person_role\CampusPersonRole;
use app\model\chat_friends\ChatFriends;
use app\model\chat_messages\ChatMessages;
use app\model\dict\Dict;
use core\base\BaseApiService;
use think\facade\Db;
/**
* 校区服务层
* Class MemberService
* @package app\service\api\member
*/
class ChatService extends BaseApiService
{
public function __construct()
{
parent::__construct();
}
//查询好友关系列表
public function getChatFriendsPage(array $where)
{
$page_params = $this->getPageParam();//获取请求参数中的页码+分页数
$page = $page_params['page'];
$limit = $page_params['limit'];
$model = new ChatFriends();
//判断用没有员工id
if (!empty($where['personnel_id'])) {
$model = $model->where('personnel_id', $where['personnel_id']);
}
if (!empty($where['customer_resources_id'])) {
$model = $model->where('customer_resources_id', $where['customer_resources_id']);
}
$data = $model->paginate([
'list_rows' => $limit,
'page' => $page,
])->toArray();
return $data;
}
//查询好友关系详情
public function getChatFriendsInfo(array $where)
{
$model = new ChatFriends();
//判断用没有员工id
if (!empty($where['personnel_id'])) {
$model = $model->where('personnel_id', $where['personnel_id']);
}
if (!empty($where['customer_resources_id'])) {
$model = $model->where('customer_resources_id', $where['customer_resources_id']);
}
$data = $model->find();
if ($data) {
$data = $data->toArray();
$res = [
'code' => 1,
'msg' => '操作成功',
'data' => $data
];
return $res;
} else {
$res = [
'code' => 0,
'msg' => '暂无数据',
'data' => []
];
return $res;
}
}
//创建好友关系
public function addChatFriends(array $data)
{
$data = ChatFriends::create($data);
if($data){
$res = [
'code' => 1,
'msg' => '操作成功',
'data' => $data
];
return $res;
}else{
$res = [
'code' => 0,
'msg' => '操作失败',
'data' => []
];
return $res;
}
}
//发送聊天信息
public function sendChatMessages(array $data){
//开启事物操作
Db::startTrans();
try {
$add = ChatMessages::create($data);
if(!empty($data['from_type']) && !empty($data['from_id']) && !empty($data['friend_id'])){
$to_type = 'personnel';
if($data['from_type'] == 'personnel'){
$to_type = 'customer';
}
$this->addUnreadCount($to_type,$data['friend_id']);
$this->editUnreadCount($data['from_type'],$data['friend_id']);
}
if($add){
Db::commit();
$res = [
'code' => 1,
'msg' => '操作成功',
'data' => $add->toArray()
];
return $res;
}else{
Db::rollback();
$res = [
'code' => 0,
'msg' => '操作失败',
'data' => []
];
return $res;
}
}catch (\Exception $exception){
Db::rollback();
$res = [
'code' => 0,
'msg' => '操作失败',
'data' => []
];
return $res;
}
}
//获取聊天记录
public function getChatMessagesList(array $where){
$page_params = $this->getPageParam();//获取请求参数中的页码+分页数
$page = $page_params['page'];
$limit = $page_params['limit'];
$model = new ChatMessages();
$model = $model->where('friend_id',$where['friend_id']);
// 按id倒序排列
$data = $model->order('id', 'desc')->paginate([
'list_rows' => $limit,
'page' => $page,
])->toArray();
return $data;
}
/**
* 修改未读消息数量
* @param $from_type 发送者类型|personnel=员工,customer=学生(客户)
* @param $from_id 发送者ID(员工/学生)
* @param $friend_id 关联chat_friends表id
*/
public function editUnreadCount($from_type ,$friend_id){
$where = [];
if($from_type == 'personnel'){
//员工发送的消息->把员工的未读消息数量清空
$data['unread_count_personnel'] = 0;
}else{
//学生发送的消息->把学生的未读消息数量清空
$data['unread_count_customer_resources'] = 0;
}
$model = ChatFriends::where('id',$friend_id);
$data['updated_at'] = date('Y-m-d H:i:s');
$model = $model->update($data);
return $model;
}
/**
* 追加接收消息的人未读消息数量+1
* @param $to_type 接收者类型|personnel=员工,customer=学生(客户)
* @param $to_id 接收者ID(员工/学生)
* @param $friend_id 关联chat_friends表id
*/
public function addUnreadCount($to_type, $friend_id)
{
$model = ChatFriends::where('id', $friend_id);
if ($to_type == 'personnel') {
// 员工接收的消息 -> 员工的未读消息数量+1
return $model->inc('unread_count_personnel')->update();
} else {
// 学生接收的消息 -> 学生的未读消息数量+1
return $model->inc('unread_count_customer_resources')->update();
}
}
}

23
niucloud/app/service/api/pay/PayService.php

@ -27,6 +27,7 @@ use core\exception\ApiException;
use think\db\exception\DataNotFoundException; use think\db\exception\DataNotFoundException;
use think\db\exception\DbException; use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException; use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use think\facade\Log; use think\facade\Log;
/** /**
@ -182,10 +183,8 @@ class PayService extends BaseApiService
$order->where(['payment_id' => $info['out_trade_no']])->update(['order_status' => 'paid','payment_time' => date("Y-m-d H:i:s")]); $order->where(['payment_id' => $info['out_trade_no']])->update(['order_status' => 'paid','payment_time' => date("Y-m-d H:i:s")]);
$order_count = $order->where(['resource_id' => $order_info['resource_id']])->count(); $order_count = $order->where(['resource_id' => $order_info['resource_id']])->count();
if($order_count <= 1){
$cust_info = $cust->where(['id' => $order_info['resource_id']])->find(); $cust_info = $cust->where(['id' => $order_info['resource_id']])->find();
if($order_count <= 1){
$sex_arr = ['male' => 1, 'female' => 2, 'other' => 0]; $sex_arr = ['male' => 1, 'female' => 2, 'other' => 0];
//首次支付创建学员 //首次支付创建学员
$student->insert([ $student->insert([
@ -198,6 +197,24 @@ class PayService extends BaseApiService
'user_id' => $order_info['resource_id'] 'user_id' => $order_info['resource_id']
]); ]);
} }
$is_rel = Db::name("class_resources_rel")->where(
['resource_id' => $order_info['resource_id'],'class_id' => $order_info['class_id']]
)->find();
if($is_rel){
Db::name("class_resources_rel")->where(['id' => $is_rel['id']])->update(['status' => 2]);
}else{
Db::name("class_resources_rel")->insert([
'class_id' => $order_info['class_id'],
'resource_id' => $order_info['resource_id'],
'campus_id' => $cust_info['campus'],
'source_type' => 'student',
'join_time' => time(),
'status' => 1,
'create_time' => date("Y-m-d H:i:s"),
'update_time' => date("Y-m-d H:i:s")
]);
}
} }
} }

35
niucloud/app/validate/class_resources_rel/ClassResourcesRel.php

@ -0,0 +1,35 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\validate\class_resources_rel;
use core\base\BaseValidate;
/**
* 班级和资源验证器
* Class ClassResourcesRel
* @package addon\app\validate\class_resources_rel
*/
class ClassResourcesRel extends BaseValidate
{
protected $rule = [
'class_id' => 'require',
];
protected $message = [
'class_id.require' => ['common_validate.require', ['class_id']],
];
protected $scene = [
"add" => ['class_id', 'resource_id', 'campus_id', 'source_type', 'status'],
"edit" => ['class_id', 'resource_id', 'campus_id', 'source_type', 'status']
];
}

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 = [

18
niucloud/app/validate/person_course_schedule/PersonCourseSchedule.php

@ -20,24 +20,18 @@ class PersonCourseSchedule extends BaseValidate
{ {
protected $rule = [ protected $rule = [
'person_id' => 'require', 'resources_id' => 'require',
'person_type' => 'require', 'schedule_id' => 'require'
'schedule_id' => 'require',
'course_date' => 'require',
'time_slot' => 'require',
]; ];
protected $message = [ protected $message = [
'person_id.require' => ['common_validate.require', ['person_id']], 'resources_id.require' => ['common_validate.require', ['person_id']],
'person_type.require' => ['common_validate.require', ['person_type']], 'schedule_id.require' => ['common_validate.require', ['schedule_id']]
'schedule_id.require' => ['common_validate.require', ['schedule_id']],
'course_date.require' => ['common_validate.require', ['course_date']],
'time_slot.require' => ['common_validate.require', ['time_slot']],
]; ];
protected $scene = [ protected $scene = [
"add" => ['person_id', 'person_type', 'schedule_id', 'course_date', 'time_slot'], "add" => ['resources_id', 'schedule_id'],
"edit" => ['person_id', 'person_type', 'schedule_id', 'course_date', 'time_slot'] "edit" => ['resources_id', 'schedule_id']
]; ];
} }

Loading…
Cancel
Save