Browse Source

Merge branch 'yuhongzhe' of ssh://gitlab.frkj.cc:222/php/zhjwxt into yuhongzhe

master
王泽彦 10 months ago
parent
commit
b21a4d3939
  1. 35
      admin/src/app/api/attendance.ts
  2. 25
      admin/src/app/api/contract.ts
  3. 33
      admin/src/app/api/exam_answers.ts
  4. 23
      admin/src/app/api/exam_papers.ts
  5. 23
      admin/src/app/api/exam_questions.ts
  6. 29
      admin/src/app/api/exam_records.ts
  7. 30
      admin/src/app/api/performance_records.ts
  8. 60
      admin/src/app/api/student.ts
  9. 46
      admin/src/app/lang/zh-cn/attendance.attendance.json
  10. 40
      admin/src/app/lang/zh-cn/contract.contract.json
  11. 33
      admin/src/app/lang/zh-cn/exam_answers.exam_answers.json
  12. 28
      admin/src/app/lang/zh-cn/exam_papers.exam_papers.json
  13. 51
      admin/src/app/lang/zh-cn/exam_questions.exam_questions.json
  14. 40
      admin/src/app/lang/zh-cn/exam_records.exam_records.json
  15. 40
      admin/src/app/lang/zh-cn/performance_records.performance_records.json
  16. 31
      admin/src/app/lang/zh-cn/student.student.json
  17. 441
      admin/src/app/views/attendance/attendance.vue
  18. 402
      admin/src/app/views/attendance/components/attendance-edit.vue
  19. 5
      admin/src/app/views/campus_person_role/campus_person_role.vue
  20. 346
      admin/src/app/views/contract/components/contract-edit.vue
  21. 386
      admin/src/app/views/contract/contract.vue
  22. 320
      admin/src/app/views/exam_answers/components/exam-answers-edit.vue
  23. 333
      admin/src/app/views/exam_answers/exam_answers.vue
  24. 485
      admin/src/app/views/exam_papers/components/exam-papers-edit.vue
  25. 295
      admin/src/app/views/exam_papers/exam_papers.vue
  26. 287
      admin/src/app/views/exam_questions/components/exam-questions-edit.vue
  27. 462
      admin/src/app/views/exam_questions/exam_questions.vue
  28. 370
      admin/src/app/views/exam_records/components/exam-records-edit.vue
  29. 390
      admin/src/app/views/exam_records/exam_records.vue
  30. 372
      admin/src/app/views/performance_records/components/performance-records-edit.vue
  31. 402
      admin/src/app/views/performance_records/performance_records.vue
  32. 315
      admin/src/app/views/student/components/student-edit.vue
  33. 240
      admin/src/app/views/student/student.vue
  34. 32
      niucloud/app/adminapi/controller/attendance/Attendance.php
  35. 15
      niucloud/app/adminapi/controller/contract/Contract.php
  36. 4
      niucloud/app/adminapi/controller/customer_resources/CustomerResources.php
  37. 18
      niucloud/app/adminapi/controller/exam_answers/ExamAnswers.php
  38. 7
      niucloud/app/adminapi/controller/exam_papers/ExamPapers.php
  39. 35
      niucloud/app/adminapi/controller/exam_questions/ExamQuestions.php
  40. 26
      niucloud/app/adminapi/controller/exam_records/ExamRecords.php
  41. 20
      niucloud/app/adminapi/controller/performance_records/PerformanceRecords.php
  42. 121
      niucloud/app/adminapi/controller/student/Student.php
  43. 9
      niucloud/app/adminapi/route/attendance.php
  44. 2
      niucloud/app/adminapi/route/contract.php
  45. 9
      niucloud/app/adminapi/route/exam_answers.php
  46. 1
      niucloud/app/adminapi/route/exam_papers.php
  47. 1
      niucloud/app/adminapi/route/exam_questions.php
  48. 7
      niucloud/app/adminapi/route/exam_records.php
  49. 7
      niucloud/app/adminapi/route/performance_records.php
  50. 44
      niucloud/app/adminapi/route/student.php
  51. 66
      niucloud/app/model/attendance/Attendance.php
  52. 132
      niucloud/app/model/contract/Contract.php
  53. 74
      niucloud/app/model/exam_answers/ExamAnswers.php
  54. 40
      niucloud/app/model/exam_papers/ExamPapers.php
  55. 147
      niucloud/app/model/exam_questions/ExamQuestions.php
  56. 80
      niucloud/app/model/exam_records/ExamRecords.php
  57. 92
      niucloud/app/model/performance_records/PerformanceRecords.php
  58. 101
      niucloud/app/model/student/Student.php
  59. 20
      niucloud/app/service/admin/attendance/AttendanceService.php
  60. 2
      niucloud/app/service/admin/contract/ContractService.php
  61. 6
      niucloud/app/service/admin/customer_resources/CustomerResourcesService.php
  62. 23
      niucloud/app/service/admin/exam_answers/ExamAnswersService.php
  63. 6
      niucloud/app/service/admin/exam_papers/ExamPapersService.php
  64. 6
      niucloud/app/service/admin/exam_questions/ExamQuestionsService.php
  65. 22
      niucloud/app/service/admin/exam_records/ExamRecordsService.php
  66. 22
      niucloud/app/service/admin/performance_records/PerformanceRecordsService.php
  67. 117
      niucloud/app/service/admin/student/StudentService.php
  68. 8
      niucloud/app/validate/attendance/Attendance.php
  69. 4
      niucloud/app/validate/contract/Contract.php
  70. 10
      niucloud/app/validate/exam_papers/ExamPapers.php
  71. 10
      niucloud/app/validate/exam_questions/ExamQuestions.php
  72. 2
      niucloud/app/validate/exam_records/ExamRecords.php
  73. 6
      niucloud/app/validate/performance_records/PerformanceRecords.php
  74. 43
      niucloud/app/validate/student/Student.php

35
admin/src/app/api/attendance.ts

@ -1,5 +1,15 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- attendance // USER_CODE_BEGIN -- attendance
/** /**
* *
@ -7,7 +17,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getAttendanceList(params: Record<string, any>) { export function getAttendanceList(params: Record<string, any>) {
return request.get(`attendance/attendance`, { params }) return request.get(`attendance/attendance`, {params})
} }
/** /**
@ -16,7 +26,7 @@ export function getAttendanceList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getAttendanceInfo(id: number) { export function getAttendanceInfo(id: number) {
return request.get(`attendance/attendance/${id}`) return request.get(`attendance/attendance/${id}`);
} }
/** /**
@ -25,10 +35,7 @@ export function getAttendanceInfo(id: number) {
* @returns * @returns
*/ */
export function addAttendance(params: Record<string, any>) { export function addAttendance(params: Record<string, any>) {
return request.post('attendance/attendance', params, { return request.post('attendance/attendance', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,10 +45,7 @@ export function addAttendance(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editAttendance(params: Record<string, any>) { export function editAttendance(params: Record<string, any>) {
return request.put(`attendance/attendance/${params.id}`, params, { return request.put(`attendance/attendance/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -50,10 +54,13 @@ export function editAttendance(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteAttendance(id: number) { export function deleteAttendance(id: number) {
return request.delete(`attendance/attendance/${id}`, { return request.delete(`attendance/attendance/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true, }
showSuccessMessage: true,
}) export function getWithCampusList(params: Record<string,any>){
return request.get('attendance/campus_all', {params})
}export function getWithPersonnelList(params: Record<string,any>){
return request.get('attendance/personnel_all', {params})
} }
// USER_CODE_END -- attendance // USER_CODE_END -- attendance

25
admin/src/app/api/contract.ts

@ -1,5 +1,9 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- contract // USER_CODE_BEGIN -- contract
/** /**
* *
@ -7,7 +11,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getContractList(params: Record<string, any>) { export function getContractList(params: Record<string, any>) {
return request.get(`contract/contract`, { params }) return request.get(`contract/contract`, {params})
} }
/** /**
@ -16,7 +20,7 @@ export function getContractList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getContractInfo(id: number) { export function getContractInfo(id: number) {
return request.get(`contract/contract/${id}`) return request.get(`contract/contract/${id}`);
} }
/** /**
@ -25,10 +29,7 @@ export function getContractInfo(id: number) {
* @returns * @returns
*/ */
export function addContract(params: Record<string, any>) { export function addContract(params: Record<string, any>) {
return request.post('contract/contract', params, { return request.post('contract/contract', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,10 +39,7 @@ export function addContract(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editContract(params: Record<string, any>) { export function editContract(params: Record<string, any>) {
return request.put(`contract/contract/${params.id}`, params, { return request.put(`contract/contract/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -50,10 +48,9 @@ export function editContract(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteContract(id: number) { export function deleteContract(id: number) {
return request.delete(`contract/contract/${id}`, { return request.delete(`contract/contract/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
// USER_CODE_END -- contract // USER_CODE_END -- contract

33
admin/src/app/api/exam_answers.ts

@ -1,5 +1,11 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- exam_answers // USER_CODE_BEGIN -- exam_answers
/** /**
* *
@ -7,7 +13,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getExamAnswersList(params: Record<string, any>) { export function getExamAnswersList(params: Record<string, any>) {
return request.get(`exam_answers/exam_answers`, { params }) return request.get(`exam_answers/exam_answers`, {params})
} }
/** /**
@ -16,7 +22,7 @@ export function getExamAnswersList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getExamAnswersInfo(id: number) { export function getExamAnswersInfo(id: number) {
return request.get(`exam_answers/exam_answers/${id}`) return request.get(`exam_answers/exam_answers/${id}`);
} }
/** /**
@ -25,10 +31,7 @@ export function getExamAnswersInfo(id: number) {
* @returns * @returns
*/ */
export function addExamAnswers(params: Record<string, any>) { export function addExamAnswers(params: Record<string, any>) {
return request.post('exam_answers/exam_answers', params, { return request.post('exam_answers/exam_answers', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,10 +41,7 @@ export function addExamAnswers(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editExamAnswers(params: Record<string, any>) { export function editExamAnswers(params: Record<string, any>) {
return request.put(`exam_answers/exam_answers/${params.id}`, params, { return request.put(`exam_answers/exam_answers/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -50,10 +50,15 @@ export function editExamAnswers(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteExamAnswers(id: number) { export function deleteExamAnswers(id: number) {
return request.delete(`exam_answers/exam_answers/${id}`, { return request.delete(`exam_answers/exam_answers/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true, }
showSuccessMessage: true,
}) export function getWithCampusList(params: Record<string,any>){
return request.get('exam_answers/campus_all', {params})
}export function getWithPersonnelList(params: Record<string,any>){
return request.get('exam_answers/personnel_all', {params})
}export function getWithExamQuestionsList(params: Record<string,any>){
return request.get('exam_answers/exam_questions_all', {params})
} }
// USER_CODE_END -- exam_answers // USER_CODE_END -- exam_answers

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

@ -1,5 +1,7 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- exam_papers // USER_CODE_BEGIN -- exam_papers
/** /**
* *
@ -7,7 +9,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getExamPapersList(params: Record<string, any>) { export function getExamPapersList(params: Record<string, any>) {
return request.get(`exam_papers/exam_papers`, { params }) return request.get(`exam_papers/exam_papers`, {params})
} }
/** /**
@ -16,7 +18,7 @@ export function getExamPapersList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getExamPapersInfo(id: number) { export function getExamPapersInfo(id: number) {
return request.get(`exam_papers/exam_papers/${id}`) return request.get(`exam_papers/exam_papers/${id}`);
} }
/** /**
@ -25,10 +27,7 @@ export function getExamPapersInfo(id: number) {
* @returns * @returns
*/ */
export function addExamPapers(params: Record<string, any>) { export function addExamPapers(params: Record<string, any>) {
return request.post('exam_papers/exam_papers', params, { return request.post('exam_papers/exam_papers', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,10 +37,7 @@ export function addExamPapers(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editExamPapers(params: Record<string, any>) { export function editExamPapers(params: Record<string, any>) {
return request.put(`exam_papers/exam_papers/${params.id}`, params, { return request.put(`exam_papers/exam_papers/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -50,10 +46,9 @@ export function editExamPapers(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteExamPapers(id: number) { export function deleteExamPapers(id: number) {
return request.delete(`exam_papers/exam_papers/${id}`, { return request.delete(`exam_papers/exam_papers/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
// USER_CODE_END -- exam_papers // USER_CODE_END -- exam_papers

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

@ -1,5 +1,7 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- exam_questions // USER_CODE_BEGIN -- exam_questions
/** /**
* *
@ -7,7 +9,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getExamQuestionsList(params: Record<string, any>) { export function getExamQuestionsList(params: Record<string, any>) {
return request.get(`exam_questions/exam_questions`, { params }) return request.get(`exam_questions/exam_questions`, {params})
} }
/** /**
@ -16,7 +18,7 @@ export function getExamQuestionsList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getExamQuestionsInfo(id: number) { export function getExamQuestionsInfo(id: number) {
return request.get(`exam_questions/exam_questions/${id}`) return request.get(`exam_questions/exam_questions/${id}`);
} }
/** /**
@ -25,10 +27,7 @@ export function getExamQuestionsInfo(id: number) {
* @returns * @returns
*/ */
export function addExamQuestions(params: Record<string, any>) { export function addExamQuestions(params: Record<string, any>) {
return request.post('exam_questions/exam_questions', params, { return request.post('exam_questions/exam_questions', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,10 +37,7 @@ export function addExamQuestions(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editExamQuestions(params: Record<string, any>) { export function editExamQuestions(params: Record<string, any>) {
return request.put(`exam_questions/exam_questions/${params.id}`, params, { return request.put(`exam_questions/exam_questions/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -50,10 +46,9 @@ export function editExamQuestions(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteExamQuestions(id: number) { export function deleteExamQuestions(id: number) {
return request.delete(`exam_questions/exam_questions/${id}`, { return request.delete(`exam_questions/exam_questions/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
// USER_CODE_END -- exam_questions // USER_CODE_END -- exam_questions

29
admin/src/app/api/exam_records.ts

@ -1,5 +1,7 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- exam_records // USER_CODE_BEGIN -- exam_records
/** /**
* *
@ -7,7 +9,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getExamRecordsList(params: Record<string, any>) { export function getExamRecordsList(params: Record<string, any>) {
return request.get(`exam_records/exam_records`, { params }) return request.get(`exam_records/exam_records`, {params})
} }
/** /**
@ -16,7 +18,7 @@ export function getExamRecordsList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getExamRecordsInfo(id: number) { export function getExamRecordsInfo(id: number) {
return request.get(`exam_records/exam_records/${id}`) return request.get(`exam_records/exam_records/${id}`);
} }
/** /**
@ -25,10 +27,7 @@ export function getExamRecordsInfo(id: number) {
* @returns * @returns
*/ */
export function addExamRecords(params: Record<string, any>) { export function addExamRecords(params: Record<string, any>) {
return request.post('exam_records/exam_records', params, { return request.post('exam_records/exam_records', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,10 +37,7 @@ export function addExamRecords(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editExamRecords(params: Record<string, any>) { export function editExamRecords(params: Record<string, any>) {
return request.put(`exam_records/exam_records/${params.id}`, params, { return request.put(`exam_records/exam_records/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -50,10 +46,15 @@ export function editExamRecords(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteExamRecords(id: number) { export function deleteExamRecords(id: number) {
return request.delete(`exam_records/exam_records/${id}`, { return request.delete(`exam_records/exam_records/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true, }
showSuccessMessage: true,
}) export function getWithCampusList(params: Record<string,any>){
return request.get('exam_records/campus_all', {params})
}export function getWithPersonnelList(params: Record<string,any>){
return request.get('exam_records/personnel_all', {params})
}export function getWithExamPapersList(params: Record<string,any>){
return request.get('exam_records/exam_papers_all', {params})
} }
// USER_CODE_END -- exam_records // USER_CODE_END -- exam_records

30
admin/src/app/api/performance_records.ts

@ -1,5 +1,7 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- performance_records // USER_CODE_BEGIN -- performance_records
/** /**
* *
@ -7,7 +9,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getPerformanceRecordsList(params: Record<string, any>) { export function getPerformanceRecordsList(params: Record<string, any>) {
return request.get(`performance_records/performance_records`, { params }) return request.get(`performance_records/performance_records`, {params})
} }
/** /**
@ -16,7 +18,7 @@ export function getPerformanceRecordsList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getPerformanceRecordsInfo(id: number) { export function getPerformanceRecordsInfo(id: number) {
return request.get(`performance_records/performance_records/${id}`) return request.get(`performance_records/performance_records/${id}`);
} }
/** /**
@ -25,10 +27,7 @@ export function getPerformanceRecordsInfo(id: number) {
* @returns * @returns
*/ */
export function addPerformanceRecords(params: Record<string, any>) { export function addPerformanceRecords(params: Record<string, any>) {
return request.post('performance_records/performance_records', params, { return request.post('performance_records/performance_records', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,11 +37,7 @@ export function addPerformanceRecords(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editPerformanceRecords(params: Record<string, any>) { export function editPerformanceRecords(params: Record<string, any>) {
return request.put( return request.put(`performance_records/performance_records/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
`performance_records/performance_records/${params.id}`,
params,
{ showErrorMessage: true, showSuccessMessage: true }
)
} }
/** /**
@ -51,10 +46,15 @@ export function editPerformanceRecords(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deletePerformanceRecords(id: number) { export function deletePerformanceRecords(id: number) {
return request.delete(`performance_records/performance_records/${id}`, { return request.delete(`performance_records/performance_records/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true, }
showSuccessMessage: true,
}) export function getWithPersonnelList(params: Record<string,any>){
return request.get('performance_records/personnel_all', {params})
}export function getWithCustomerResourcesList(params: Record<string,any>){
return request.get('performance_records/customer_resources_all', {params})
}export function getWithOrderTableList(params: Record<string,any>){
return request.get('performance_records/order_table_all', {params})
} }
// USER_CODE_END -- performance_records // USER_CODE_END -- performance_records

60
admin/src/app/api/student.ts

@ -0,0 +1,60 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- student
/**
*
* @param params
* @returns
*/
export function getStudentList(params: Record<string, any>) {
return request.get(`student/student`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getStudentInfo(id: number) {
return request.get(`student/student/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addStudent(params: Record<string, any>) {
return request.post('student/student', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editStudent(params: Record<string, any>) {
return request.put(`student/student/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteStudent(id: number) {
return request.delete(`student/student/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithCampusList(params: Record<string,any>){
return request.get('student/campus_all', {params})
}export function getWithClassGradeList(params: Record<string,any>){
return request.get('student/class_grade_all', {params})
}export function getWithMemberList(params: Record<string,any>){
return request.get('student/member_all', {params})
}
// USER_CODE_END -- student

46
admin/src/app/lang/zh-cn/attendance.attendance.json

@ -1,29 +1,21 @@
{ {
"id": "考勤编号", "campusId":"校区",
"idPlaceholder": "请输入考勤编号", "campusIdPlaceholder":"全部",
"campusId": "校区ID", "staffId":"人员",
"campusIdPlaceholder": "请输入校区ID", "staffIdPlaceholder":"全部",
"staffId": "人员ID", "attendanceDate":"考勤日期",
"staffIdPlaceholder": "请输入人员ID", "attendanceDatePlaceholder":"请输入考勤日期",
"attendanceDate": "考勤日期", "checkInTime":"签到时间",
"attendanceDatePlaceholder": "请输入考勤日期", "checkInTimePlaceholder":"请输入签到时间",
"checkInTime": "签到时间", "checkOutTime":"签退时间",
"checkInTimePlaceholder": "请输入签到时间", "checkOutTimePlaceholder":"请输入签退时间",
"checkOutTime": "签退时间", "remarks":"备注",
"checkOutTimePlaceholder": "请输入签退时间", "remarksPlaceholder":"请输入备注",
"status": "考勤状态", "status":"考勤状态",
"statusPlaceholder": "请输入考勤状态", "statusPlaceholder":"请输入考勤状态",
"remarks": "备注", "addAttendance":"添加考勤",
"remarksPlaceholder": "请输入备注", "updateAttendance":"编辑考勤",
"createdAt": "创建时间", "attendanceDeleteTips":"确定要删除该数据吗?",
"createdAtPlaceholder": "请输入创建时间", "startDate":"请选择开始时间",
"updatedAt": "修改时间", "endDate":"请选择结束时间"
"updatedAtPlaceholder": "请输入修改时间",
"coordinate": "坐标",
"coordinatePlaceholder": "请输入坐标",
"addAttendance": "添加考勤",
"updateAttendance": "编辑考勤",
"attendanceDeleteTips": "确定要删除该数据吗?",
"startDate": "请选择开始时间",
"endDate": "请选择结束时间"
} }

40
admin/src/app/lang/zh-cn/contract.contract.json

@ -1,25 +1,19 @@
{ {
"id": "合同编号", "contractName":"合同名称",
"idPlaceholder": "请输入合同编号", "contractNamePlaceholder":"请输入合同名称",
"contractName": "合同名称", "contractTemplate":"合同模板",
"contractNamePlaceholder": "请输入合同名称", "contractTemplatePlaceholder":"请输入合同模板",
"contractTemplate": "合同模板", "contractStatus":"合同状态",
"contractTemplatePlaceholder": "请输入合同模板", "contractStatusPlaceholder":"请输入合同状态",
"contractStatus": "合同状态", "contractType":"合同类型",
"contractStatusPlaceholder": "请输入合同状态", "contractTypePlaceholder":"请输入合同类型",
"contractType": "合同类型", "remarks":"合同备注",
"contractTypePlaceholder": "请输入合同类型", "remarksPlaceholder":"请输入合同备注",
"remarks": "合同备注", "createdAt":"创建时间",
"remarksPlaceholder": "请输入合同备注", "createdAtPlaceholder":"请输入创建时间",
"createdAt": "创建时间", "addContract":"添加合同",
"createdAtPlaceholder": "请输入创建时间", "updateContract":"编辑合同",
"updatedAt": "修改时间", "contractDeleteTips":"确定要删除该数据吗?",
"updatedAtPlaceholder": "请输入修改时间", "startDate":"请选择开始时间",
"deletedAt": "逻辑删除时间", "endDate":"请选择结束时间"
"deletedAtPlaceholder": "请输入逻辑删除时间",
"addContract": "添加合同",
"updateContract": "编辑合同",
"contractDeleteTips": "确定要删除该数据吗?",
"startDate": "请选择开始时间",
"endDate": "请选择结束时间"
} }

33
admin/src/app/lang/zh-cn/exam_answers.exam_answers.json

@ -1,19 +1,18 @@
{ {
"id": "答题记录编号", "campusId":"校区",
"idPlaceholder": "请输入答题记录编号", "campusIdPlaceholder":"全部",
"campusId": "校区ID", "userId":"人员",
"campusIdPlaceholder": "请输入校区ID", "userIdPlaceholder":"请输入人员",
"userId": "人员ID", "questionId":"试题",
"userIdPlaceholder": "请输入人员ID", "questionIdPlaceholder":"请输入试题",
"questionId": "试题ID", "answer":"用户答案",
"questionIdPlaceholder": "请输入试题ID", "answerPlaceholder":"请输入用户答案",
"answer": "用户答案", "isCorrect":"是否正确",
"answerPlaceholder": "请输入用户答案", "isCorrectPlaceholder":"请输入是否正确",
"isCorrect": "是否正确", "createdAt":"创建时间",
"isCorrectPlaceholder": "请输入是否正确", "addExamAnswers":"添加答题记录",
"addExamAnswers": "添加答题记录", "updateExamAnswers":"编辑答题记录",
"updateExamAnswers": "编辑答题记录", "examAnswersDeleteTips":"确定要删除该数据吗?",
"examAnswersDeleteTips": "确定要删除该数据吗?", "startDate":"请选择开始时间",
"startDate": "请选择开始时间", "endDate":"请选择结束时间"
"endDate": "请选择结束时间"
} }

28
admin/src/app/lang/zh-cn/exam_papers.exam_papers.json

@ -1,15 +1,17 @@
{ {
"id": "试卷编号", "selectionMode":"题目选择模式",
"idPlaceholder": "请输入试卷编号", "selectionModePlaceholder":"请输入题目选择模式",
"selectionMode": "题目选择模式: random-随机主题, manual-自选题目", "questionsIds":"自选试题",
"selectionModePlaceholder": "请输入题目选择模式: random-随机主题, manual-自选题目", "questionsIdsPlaceholder":"请输入自选试题",
"totalScore": "总分", "totalScore":"总分",
"totalScorePlaceholder": "请输入总分", "totalScorePlaceholder":"请输入总分",
"passingScore": "合格分数", "passingScore":"合格分数",
"passingScorePlaceholder": "请输入合格分数", "passingScorePlaceholder":"请输入合格分数",
"addExamPapers": "添加试卷", "createdAt":"创建时间",
"updateExamPapers": "编辑试卷", "createdAtPlaceholder":"请输入创建时间",
"examPapersDeleteTips": "确定要删除该数据吗?", "addExamPapers":"添加试卷",
"startDate": "请选择开始时间", "updateExamPapers":"编辑试卷",
"endDate": "请选择结束时间" "examPapersDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
} }

51
admin/src/app/lang/zh-cn/exam_questions.exam_questions.json

@ -1,33 +1,22 @@
{ {
"id": "试题编号", "title":"题目标题",
"idPlaceholder": "请输入试题编号", "titlePlaceholder":"请输入题目标题",
"questionType": "题型: single_choice-单选, multiple_choice-多选, true_false-判断", "questionType":"题型",
"questionTypePlaceholder": "请输入题型: single_choice-单选, multiple_choice-多选, true_false-判断", "questionTypePlaceholder":"请输入题型",
"questionContentType": "题干类型: text-文本, image-图片", "questionContentType":"题干类型",
"questionContentTypePlaceholder": "请输入题干类型: text-文本, image-图片", "questionContentTypePlaceholder":"请输入题干类型",
"questionContent": "题干内容(如果是图片则存储URL)", "questionContent":"题干内容",
"questionContentPlaceholder": "请输入题干内容(如果是图片则存储URL)", "questionContentPlaceholder":"请输入题干内容",
"optionAContentType": "选项A类型: text-文本, image-图片", "optionJson":"选项",
"optionAContentTypePlaceholder": "请输入选项A类型: text-文本, image-图片", "optionJsonPlaceholder":"请输入选项",
"optionAContent": "选项A内容(如果是图片则存储URL)", "correctAnswer":"正确答案",
"optionAContentPlaceholder": "请输入选项A内容(如果是图片则存储URL)", "correctAnswerPlaceholder":"请输入正确答案",
"optionBContentType": "选项B类型: text-文本, image-图片", "createdAt":"创建时间",
"optionBContentTypePlaceholder": "请输入选项B类型: text-文本, image-图片", "createdAtPlaceholder":"请输入创建时间",
"optionBContent": "选项B内容(如果是图片则存储URL)", "updatedAt":"修改时间",
"optionBContentPlaceholder": "请输入选项B内容(如果是图片则存储URL)", "addExamQuestions":"添加试题",
"optionCContentType": "选项C类型: text-文本, image-图片", "updateExamQuestions":"编辑试题",
"optionCContentTypePlaceholder": "请输入选项C类型: text-文本, image-图片", "examQuestionsDeleteTips":"确定要删除该数据吗?",
"optionCContent": "选项C内容(如果是图片则存储URL)", "startDate":"请选择开始时间",
"optionCContentPlaceholder": "请输入选项C内容(如果是图片则存储URL)", "endDate":"请选择结束时间"
"optionDContentType": "选项D类型: text-文本, image-图片",
"optionDContentTypePlaceholder": "请输入选项D类型: text-文本, image-图片",
"optionDContent": "选项D内容(如果是图片则存储URL)",
"optionDContentPlaceholder": "请输入选项D内容(如果是图片则存储URL)",
"correctAnswer": "正确答案(如果是多选,答案格式为如A,B,D)",
"correctAnswerPlaceholder": "请输入正确答案(如果是多选,答案格式为如A,B,D)",
"addExamQuestions": "添加试题",
"updateExamQuestions": "编辑试题",
"examQuestionsDeleteTips": "确定要删除该数据吗?",
"startDate": "请选择开始时间",
"endDate": "请选择结束时间"
} }

40
admin/src/app/lang/zh-cn/exam_records.exam_records.json

@ -1,23 +1,21 @@
{ {
"id": "记录编号", "campusId":"校区",
"idPlaceholder": "请输入记录编号", "campusIdPlaceholder":"全部",
"campusId": "校区ID", "userId":"人员",
"campusIdPlaceholder": "请输入校区ID", "userIdPlaceholder":"请输入人员",
"userId": "人员ID", "paperId":"试卷",
"userIdPlaceholder": "请输入人员ID", "paperIdPlaceholder":"全部",
"paperId": "试卷ID", "score":"得分",
"paperIdPlaceholder": "请输入试卷ID", "scorePlaceholder":"请输入得分",
"score": "得分", "status":"考试状态",
"scorePlaceholder": "请输入得分", "statusPlaceholder":"请输入考试状态",
"status": "考试状态: in_progress-进行中, completed-已完成", "startTime":"考试开始时间",
"statusPlaceholder": "请输入考试状态: in_progress-进行中, completed-已完成", "startTimePlaceholder":"请输入考试开始时间",
"startTime": "考试开始时间", "endTime":"考试结束时间",
"startTimePlaceholder": "请输入考试开始时间", "endTimePlaceholder":"请输入考试结束时间",
"endTime": "考试结束时间", "addExamRecords":"添加考试记录",
"endTimePlaceholder": "请输入考试结束时间", "updateExamRecords":"编辑考试记录",
"addExamRecords": "添加考试记录", "examRecordsDeleteTips":"确定要删除该数据吗?",
"updateExamRecords": "编辑考试记录", "startDate":"请选择开始时间",
"examRecordsDeleteTips": "确定要删除该数据吗?", "endDate":"请选择结束时间"
"startDate": "请选择开始时间",
"endDate": "请选择结束时间"
} }

40
admin/src/app/lang/zh-cn/performance_records.performance_records.json

@ -1,23 +1,21 @@
{ {
"id": "绩效编号", "staffId":"员工",
"idPlaceholder": "请输入绩效编号", "staffIdPlaceholder":"全部",
"staffId": "员工ID", "resourceId":"资源",
"staffIdPlaceholder": "请输入员工ID", "resourceIdPlaceholder":"请输入资源",
"resourceId": "资源ID", "orderStatus":"订单状态",
"resourceIdPlaceholder": "请输入资源ID", "orderStatusPlaceholder":"请输入订单状态",
"orderId": "订单ID", "performanceType":"绩效类型",
"orderIdPlaceholder": "请输入订单ID", "performanceTypePlaceholder":"请输入绩效类型",
"orderStatus": "订单状态: pending-待处理, completed-已完成, cancelled-已取消", "performanceValue":"绩效金额或分值",
"orderStatusPlaceholder": "请输入订单状态: pending-待处理, completed-已完成, cancelled-已取消", "performanceValuePlaceholder":"请输入绩效金额或分值",
"performanceType": "绩效类型: sales-销售绩效, marketing-市场绩效, other-其他", "remarks":"备注",
"performanceTypePlaceholder": "请输入绩效类型: sales-销售绩效, marketing-市场绩效, other-其他", "remarksPlaceholder":"请输入备注",
"performanceValue": "绩效金额或分值", "createdAt":"创建时间",
"performanceValuePlaceholder": "请输入绩效金额或分值", "createdAtPlaceholder":"请输入创建时间",
"remarks": "备注", "addPerformanceRecords":"添加绩效记录",
"remarksPlaceholder": "请输入备注", "updatePerformanceRecords":"编辑绩效记录",
"addPerformanceRecords": "添加绩效记录", "performanceRecordsDeleteTips":"确定要删除该数据吗?",
"updatePerformanceRecords": "编辑绩效记录", "startDate":"请选择开始时间",
"performanceRecordsDeleteTips": "确定要删除该数据吗?", "endDate":"请选择结束时间"
"startDate": "请选择开始时间",
"endDate": "请选择结束时间"
} }

31
admin/src/app/lang/zh-cn/student.student.json

@ -0,0 +1,31 @@
{
"campusId":"校区",
"campusIdPlaceholder":"全部",
"classId":"班级",
"classIdPlaceholder":"请输入班级",
"userId":"用户",
"userIdPlaceholder":"请输入用户",
"name":"学员姓名",
"namePlaceholder":"请输入学员姓名",
"gender":"性别",
"genderPlaceholder":"请输入性别",
"age":"年龄",
"agePlaceholder":"请输入年龄",
"birthday":"生日",
"birthdayPlaceholder":"请输入生日",
"emergencyContact":"紧急联系人",
"emergencyContactPlaceholder":"请输入紧急联系人",
"contactPhone":"联系人电话",
"contactPhonePlaceholder":"请输入联系人电话",
"note":"备注信息",
"notePlaceholder":"请输入备注信息",
"status":"学员状态",
"statusPlaceholder":"请输入学员状态",
"createdAt":"创建时间",
"createdAtPlaceholder":"请输入创建时间",
"addStudent":"添加学员",
"updateStudent":"编辑学员",
"studentDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

441
admin/src/app/views/attendance/attendance.vue

@ -1,235 +1,142 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<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">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addAttendance') }} <!-- <el-button type="primary" @click="addEvent">
</el-button> {{ t('addAttendance') }}
</div> </el-button> -->
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="attendanceTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('campusId')" prop="campus_id">
:inline="true" <el-select class="w-[280px]" v-model="attendanceTable.searchParam.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
:model="attendanceTable.searchParam" <el-option
ref="searchFormRef" v-for="(item, index) in campusIdList"
> :key="index"
<el-form-item :label="t('campusId')" prop="campus_id"> :label="item['campus_name']"
<el-input :value="item['id']"
v-model="attendanceTable.searchParam.campus_id" />
:placeholder="t('campusIdPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('staffId')" prop="staff_id">
<el-input <el-form-item :label="t('staffId')" prop="staff_id">
v-model="attendanceTable.searchParam.staff_id" <el-select class="w-[280px]" v-model="attendanceTable.searchParam.staff_id" clearable :placeholder="t('staffIdPlaceholder')">
:placeholder="t('staffIdPlaceholder')" <el-option
/> v-for="(item, index) in staffIdList"
</el-form-item> :key="index"
<el-form-item :label="t('attendanceDate')" prop="attendance_date"> :label="item['name']"
<el-input :value="item['id']"
v-model="attendanceTable.searchParam.attendance_date" />
:placeholder="t('attendanceDatePlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('checkInTime')" prop="check_in_time"> <el-form-item :label="t('attendanceDate')" prop="attendance_date">
<el-input
v-model="attendanceTable.searchParam.check_in_time" <el-date-picker
:placeholder="t('checkInTimePlaceholder')" class="flex-1 !flex"
/> v-model="attendanceTable.searchParam.attendance_date"
</el-form-item> clearable
<el-form-item :label="t('checkOutTime')" prop="check_out_time"> type="date"
<el-input value-format="YYYY-MM-DD"
v-model="attendanceTable.searchParam.check_out_time" :placeholder="t('attendanceDate')">
:placeholder="t('checkOutTimePlaceholder')" </el-date-picker>
/>
</el-form-item> </el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-input
v-model="attendanceTable.searchParam.status" <el-form-item :label="t('status')" prop="status">
:placeholder="t('statusPlaceholder')" <el-select class="w-[280px]" v-model="attendanceTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
/> <el-option label="全部" value=""></el-option>
</el-form-item> <el-option
<el-form-item :label="t('remarks')" prop="remarks"> v-for="(item, index) in statusList"
<el-input :key="index"
v-model="attendanceTable.searchParam.remarks" :label="item.name"
:placeholder="t('remarksPlaceholder')" :value="item.value"
/> />
</el-form-item> </el-select>
<el-form-item :label="t('createdAt')" prop="created_at"> </el-form-item>
<el-input
v-model="attendanceTable.searchParam.created_at" <el-form-item>
:placeholder="t('createdAtPlaceholder')" <el-button type="primary" @click="loadAttendanceList()">{{ t('search') }}</el-button>
/> <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item> </el-form-item>
<el-form-item :label="t('updatedAt')" prop="updated_at"> </el-form>
<el-input </el-card>
v-model="attendanceTable.searchParam.updated_at"
:placeholder="t('updatedAtPlaceholder')" <div class="mt-[10px]">
/> <el-table :data="attendanceTable.data" size="large" v-loading="attendanceTable.loading">
</el-form-item> <template #empty>
<el-form-item :label="t('coordinate')" prop="coordinate"> <span>{{ !attendanceTable.loading ? t('emptyData') : '' }}</span>
<el-input </template>
v-model="attendanceTable.searchParam.coordinate" <el-table-column prop="campus_id_name" :label="t('campusId')" min-width="120" :show-overflow-tooltip="true"/>
:placeholder="t('coordinatePlaceholder')"
/> <el-table-column prop="staff_id_name" :label="t('staffId')" min-width="120" :show-overflow-tooltip="true"/>
</el-form-item>
<el-table-column prop="attendance_date" :label="t('attendanceDate')" min-width="120" :show-overflow-tooltip="true"/>
<el-form-item>
<el-button type="primary" @click="loadAttendanceList()">{{ <el-table-column prop="check_in_time" :label="t('checkInTime')" min-width="120" :show-overflow-tooltip="true"/>
t('search')
}}</el-button> <el-table-column prop="check_out_time" :label="t('checkOutTime')" min-width="120" :show-overflow-tooltip="true"/>
<el-button @click="resetForm(searchFormRef)">{{
t('reset') <el-table-column prop="remarks" :label="t('remarks')" min-width="120" :show-overflow-tooltip="true"/>
}}</el-button>
</el-form-item> <el-table-column :label="t('status')" min-width="180" align="center" :show-overflow-tooltip="true">
</el-form> <template #default="{ row }">
</el-card> <div v-for="(item, index) in statusList">
<div v-if="item.value == row.status">{{ item.name }}</div>
<div class="mt-[10px]"> </div>
<el-table </template>
:data="attendanceTable.data" </el-table-column>
size="large"
v-loading="attendanceTable.loading" <!-- <el-table-column :label="t('operation')" fixed="right" min-width="120">
> <template #default="{ row }">
<template #empty> <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<span>{{ !attendanceTable.loading ? t('emptyData') : '' }}</span> <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template> </template>
<el-table-column </el-table-column> -->
prop="campus_id"
:label="t('campusId')" </el-table>
min-width="120" <div class="mt-[16px] flex justify-end">
:show-overflow-tooltip="true" <el-pagination v-model:current-page="attendanceTable.page" v-model:page-size="attendanceTable.limit"
/> layout="total, sizes, prev, pager, next, jumper" :total="attendanceTable.total"
@size-change="loadAttendanceList()" @current-change="loadAttendanceList" />
<el-table-column </div>
prop="staff_id" </div>
:label="t('staffId')"
min-width="120" <edit ref="editAttendanceDialog" @complete="loadAttendanceList" />
:show-overflow-tooltip="true" </el-card>
/> </div>
<el-table-column
prop="attendance_date"
:label="t('attendanceDate')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="check_in_time"
:label="t('checkInTime')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="check_out_time"
:label="t('checkOutTime')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="status"
:label="t('status')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="remarks"
:label="t('remarks')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="created_at"
:label="t('createdAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="updated_at"
:label="t('updatedAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="coordinate"
:label="t('coordinate')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="attendanceTable.page"
v-model:page-size="attendanceTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="attendanceTable.total"
@size-change="loadAttendanceList()"
@current-change="loadAttendanceList"
/>
</div>
</div>
<edit ref="editAttendanceDialog" @complete="loadAttendanceList" />
</el-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
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 { getAttendanceList, deleteAttendance } from '@/app/api/attendance' import { getAttendanceList, deleteAttendance, getWithCampusList, getWithPersonnelList } from '@/app/api/attendance'
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/attendance/components/attendance-edit.vue' import Edit from '@/app/views/attendance/components/attendance-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;
let attendanceTable = reactive({ let attendanceTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam:{
campus_id: '', "campus_id":"",
staff_id: '', "staff_id":"",
attendance_date: '', "attendance_date":"",
check_in_time: '', "status":""
check_out_time: '', }
status: '',
remarks: '',
created_at: '',
updated_at: '',
coordinate: '',
},
}) })
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
@ -238,26 +145,29 @@ const searchFormRef = ref<FormInstance>()
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
// //
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('kq_status')).data.dictionary
}
statusDictList();
/** /**
* 获取考勤列表 * 获取考勤列表
*/ */
const loadAttendanceList = (page: number = 1) => { const loadAttendanceList = (page: number = 1) => {
attendanceTable.loading = true attendanceTable.loading = true
attendanceTable.page = page attendanceTable.page = page
getAttendanceList({ getAttendanceList({
page: attendanceTable.page, page: attendanceTable.page,
limit: attendanceTable.limit, limit: attendanceTable.limit,
...attendanceTable.searchParam, ...attendanceTable.searchParam
}) }).then(res => {
.then((res) => { attendanceTable.loading = false
attendanceTable.loading = false attendanceTable.data = res.data.data
attendanceTable.data = res.data.data attendanceTable.total = res.data.total
attendanceTable.total = res.data.total }).catch(() => {
}) attendanceTable.loading = false
.catch(() => {
attendanceTable.loading = false
}) })
} }
loadAttendanceList() loadAttendanceList()
@ -268,8 +178,8 @@ const editAttendanceDialog: Record<string, any> | null = ref(null)
* 添加考勤 * 添加考勤
*/ */
const addEvent = () => { const addEvent = () => {
editAttendanceDialog.value.setFormData() editAttendanceDialog.value.setFormData()
editAttendanceDialog.value.showDialog = true editAttendanceDialog.value.showDialog = true
} }
/** /**
@ -277,42 +187,55 @@ const addEvent = () => {
* @param data * @param data
*/ */
const editEvent = (data: any) => { const editEvent = (data: any) => {
editAttendanceDialog.value.setFormData(data) editAttendanceDialog.value.setFormData(data)
editAttendanceDialog.value.showDialog = true editAttendanceDialog.value.showDialog = true
} }
/** /**
* 删除考勤 * 删除考勤
*/ */
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('attendanceDeleteTips'), t('warning'), { ElMessageBox.confirm(t('attendanceDeleteTips'), t('warning'),
confirmButtonText: t('confirm'), {
cancelButtonText: t('cancel'), confirmButtonText: t('confirm'),
type: 'warning', cancelButtonText: t('cancel'),
}).then(() => { type: 'warning',
deleteAttendance(id) }
.then(() => { ).then(() => {
loadAttendanceList() deleteAttendance(id).then(() => {
}) loadAttendanceList()
.catch(() => {}) }).catch(() => {
}) })
})
} }
const campusIdList = ref([])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const staffIdList = ref([])
const setStaffIdList = async () => {
staffIdList.value = await (await getWithPersonnelList({})).data
}
setStaffIdList()
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
formEl.resetFields() formEl.resetFields()
loadAttendanceList() loadAttendanceList()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */ /* 多行超出隐藏 */
.multi-hidden { .multi-hidden {
word-break: break-all; word-break: break-all;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
</style> </style>

402
admin/src/app/views/attendance/components/attendance-edit.vue

@ -1,122 +1,87 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateAttendance') : t('addAttendance')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
v-model="showDialog" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
:title="formData.id ? t('updateAttendance') : t('addAttendance')" <el-form-item :label="t('campusId')" prop="campus_id">
width="50%" <el-select class="input-width" v-model="formData.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
class="diy-dialog-wrap" <el-option label="请选择" value=""></el-option>
:destroy-on-close="true" <el-option
> v-for="(item, index) in campusIdList"
<el-form :key="index"
:model="formData" :label="item['campus_name']"
label-width="120px" :value="item['id']"
ref="formRef" />
:rules="formRules" </el-select>
class="page-form" </el-form-item>
v-loading="loading"
>
<el-form-item :label="t('campusId')" prop="campus_id">
<el-input
v-model="formData.campus_id"
clearable
:placeholder="t('campusIdPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('staffId')" prop="staff_id"> <el-form-item :label="t('staffId')" prop="staff_id">
<el-input <el-select class="input-width" v-model="formData.staff_id" clearable :placeholder="t('staffIdPlaceholder')">
v-model="formData.staff_id" <el-option label="请选择" value=""></el-option>
clearable <el-option
:placeholder="t('staffIdPlaceholder')" v-for="(item, index) in staffIdList"
class="input-width" :key="index"
/> :label="item['name']"
</el-form-item> :value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('attendanceDate')" prop="attendance_date"> <el-form-item :label="t('attendanceDate')" prop="attendance_date" class="input-width">
<el-input <el-date-picker
v-model="formData.attendance_date" class="flex-1 !flex"
clearable v-model="formData.attendance_date"
:placeholder="t('attendanceDatePlaceholder')" clearable
class="input-width" type="datetime"
/> value-format="YYYY-MM-DD HH:mm:ss"
</el-form-item> :placeholder="t('attendanceDatePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('checkInTime')" prop="check_in_time" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.check_in_time"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('checkInTimePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('checkOutTime')" prop="check_out_time" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.check_out_time"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('checkOutTimePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('remarks')" >
<el-input v-model="formData.remarks" clearable :placeholder="t('remarksPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('checkInTime')"> <el-form-item :label="t('status')" prop="status">
<el-input <el-select class="input-width" v-model="formData.status" clearable :placeholder="t('statusPlaceholder')">
v-model="formData.check_in_time" <el-option label="请选择" value=""></el-option>
clearable <el-option
:placeholder="t('checkInTimePlaceholder')" v-for="(item, index) in statusList"
class="input-width" :key="index"
/> :label="item.name"
</el-form-item> :value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('checkOutTime')"> </el-form>
<el-input
v-model="formData.check_out_time"
clearable
:placeholder="t('checkOutTimePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('status')" prop="status"> <template #footer>
<el-input <span class="dialog-footer">
v-model="formData.status" <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
clearable <el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
:placeholder="t('statusPlaceholder')" t('confirm')
class="input-width" }}</el-button>
/> </span>
</el-form-item> </template>
</el-dialog>
<el-form-item :label="t('remarks')">
<el-input
v-model="formData.remarks"
clearable
:placeholder="t('remarksPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('createdAt')">
<el-input
v-model="formData.created_at"
clearable
:placeholder="t('createdAtPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('updatedAt')">
<el-input
v-model="formData.updated_at"
clearable
:placeholder="t('updatedAtPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('coordinate')">
<el-input
v-model="formData.coordinate"
clearable
:placeholder="t('coordinatePlaceholder')"
class="input-width"
/>
</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> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -124,11 +89,7 @@ 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 { import { addAttendance, editAttendance, getAttendanceInfo, getWithCampusList, getWithPersonnelList } from '@/app/api/attendance'
addAttendance,
editAttendance,
getAttendanceInfo,
} from '@/app/api/attendance'
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
@ -137,17 +98,14 @@ const loading = ref(false)
* 表单数据 * 表单数据
*/ */
const initialFormData = { const initialFormData = {
id: '', id: '',
campus_id: '', campus_id: '',
staff_id: '', staff_id: '',
attendance_date: '', attendance_date: '',
check_in_time: '', check_in_time: '',
check_out_time: '', check_out_time: '',
status: '', remarks: '',
remarks: '', status: '',
created_at: '',
updated_at: '',
coordinate: '',
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -155,46 +113,43 @@ const formRef = ref<FormInstance>()
// //
const formRules = computed(() => { const formRules = computed(() => {
return { return {
campus_id: [ campus_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
],
]
,
staff_id: [ staff_id: [
{ required: true, message: t('staffIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('staffIdPlaceholder'), trigger: 'blur' },
],
]
,
attendance_date: [ attendance_date: [
{ { required: true, message: t('attendanceDatePlaceholder'), trigger: 'blur' },
required: true,
message: t('attendanceDatePlaceholder'), ]
trigger: 'blur', ,
},
],
check_in_time: [ check_in_time: [
{ required: true, message: t('checkInTimePlaceholder'), trigger: 'blur' }, { required: true, message: t('checkInTimePlaceholder'), trigger: 'blur' },
],
]
,
check_out_time: [ check_out_time: [
{ { required: true, message: t('checkOutTimePlaceholder'), trigger: 'blur' },
required: true,
message: t('checkOutTimePlaceholder'), ]
trigger: 'blur', ,
},
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
remarks: [ remarks: [
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' }, { required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
],
created_at: [ ]
{ required: true, message: t('createdAtPlaceholder'), trigger: 'blur' }, ,
], status: [
updated_at: [ { required: true, message: t('statusPlaceholder'), trigger: 'blur' },
{ required: true, message: t('updatedAtPlaceholder'), trigger: 'blur' },
], ]
coordinate: [ ,
{ required: true, message: t('coordinatePlaceholder'), trigger: 'blur' }, }
],
}
}) })
const emit = defineEmits(['complete']) const emit = defineEmits(['complete'])
@ -204,93 +159,102 @@ const emit = defineEmits(['complete'])
* @param formEl * @param formEl
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
let save = formData.id ? editAttendance : addAttendance let save = formData.id ? editAttendance : addAttendance
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
let data = formData let data = formData
save(data) save(data).then(res => {
.then((res) => { loading.value = false
loading.value = false showDialog.value = false
showDialog.value = false emit('complete')
emit('complete') }).catch(err => {
}) loading.value = false
.catch((err) => { })
loading.value = false }
}) })
}
})
} }
// //
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('kq_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const campusIdList = ref([] as any[])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const staffIdList = ref([] as any[])
const setStaffIdList = async () => {
staffIdList.value = await (await getWithPersonnelList({})).data
}
setStaffIdList()
const setFormData = async (row: any = null) => { const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if (row) { if(row){
const data = await (await getAttendanceInfo(row.id)).data const data = await (await getAttendanceInfo(row.id)).data
if (data) if (data) Object.keys(formData).forEach((key: string) => {
Object.keys(formData).forEach((key: string) => { if (data[key] != undefined) formData[key] = data[key]
if (data[key] != undefined) formData[key] = data[key] })
}) }
} loading.value = false
loading.value = false
} }
// //
const mobileVerify = (rule: any, value: any, callback: any) => { const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) { if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile'))) callback(new Error(t('generateMobile')))
} else { } else {
callback() callback()
} }
} }
// //
const idCardVerify = (rule: any, value: any, callback: any) => { const idCardVerify = (rule: any, value: any, callback: any) => {
if ( 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)) {
value && callback(new Error(t('generateIdCard')))
!/^[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( } else {
value callback()
) }
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
} }
// //
const emailVerify = (rule: any, value: any, callback: any) => { const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail'))) callback(new Error(t('generateEmail')))
} else { } else {
callback() callback()
} }
} }
// //
const numberVerify = (rule: any, value: any, callback: any) => { const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) { if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber'))) callback(new Error(t('generateNumber')))
} else { } else {
callback() callback()
} }
} }
defineExpose({ defineExpose({
showDialog, showDialog,
setFormData, setFormData
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></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;
} }
</style> </style>

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

@ -49,7 +49,7 @@
</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">
<el-select <el-select
class="w-[280px]" class="w-[280px]"
v-model="campusPersonRoleTable.searchParam.role_id" v-model="campusPersonRoleTable.searchParam.role_id"
@ -63,7 +63,7 @@
:value="item['role_id']" :value="item['role_id']"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item> -->
<!-- <el-form-item :label="t('deptId')" prop="dept_id"> <!-- <el-form-item :label="t('deptId')" prop="dept_id">
<el-select <el-select
@ -205,6 +205,7 @@ if(pageName == '市场人员列表'){
campusPersonRoleTable.searchParam.dept_id = 1; campusPersonRoleTable.searchParam.dept_id = 1;
}else if(pageName == '销售人员列表'){ }else if(pageName == '销售人员列表'){
campusPersonRoleTable.searchParam.dept_id = 2; campusPersonRoleTable.searchParam.dept_id = 2;
campusPersonRoleTable.searchParam.role_id = 2;
}else if(pageName == '教练管理'){ }else if(pageName == '教练管理'){
campusPersonRoleTable.searchParam.role_id = 5; campusPersonRoleTable.searchParam.role_id = 5;
} }

346
admin/src/app/views/contract/components/contract-edit.vue

@ -1,104 +1,53 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateContract') : t('addContract')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
v-model="showDialog" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
:title="formData.id ? t('updateContract') : t('addContract')" <el-form-item :label="t('contractName')" prop="contract_name">
width="50%" <el-input v-model="formData.contract_name" clearable :placeholder="t('contractNamePlaceholder')" class="input-width" />
class="diy-dialog-wrap" </el-form-item>
:destroy-on-close="true"
> <el-form-item :label="t('contractTemplate')">
<el-form <upload-file v-model="formData.contract_template" />
:model="formData" </el-form-item>
label-width="120px"
ref="formRef" <el-form-item :label="t('contractStatus')" prop="contract_status">
:rules="formRules" <el-select class="input-width" v-model="formData.contract_status" clearable :placeholder="t('contractStatusPlaceholder')">
class="page-form" <el-option label="请选择" value=""></el-option>
v-loading="loading" <el-option
> v-for="(item, index) in contract_statusList"
<el-form-item :label="t('contractName')" prop="contract_name"> :key="index"
<el-input :label="item.name"
v-model="formData.contract_name" :value="item.value"
clearable />
:placeholder="t('contractNamePlaceholder')" </el-select>
class="input-width" </el-form-item>
/>
</el-form-item> <el-form-item :label="t('contractType')" prop="contract_type">
<el-select class="input-width" v-model="formData.contract_type" clearable :placeholder="t('contractTypePlaceholder')">
<el-form-item :label="t('contractTemplate')" prop="contract_template"> <el-option label="请选择" value=""></el-option>
<el-input <el-option
v-model="formData.contract_template" v-for="(item, index) in contract_typeList"
clearable :key="index"
:placeholder="t('contractTemplatePlaceholder')" :label="item.name"
class="input-width" :value="item.value"
/> />
</el-form-item> </el-select>
</el-form-item>
<el-form-item :label="t('contractStatus')" prop="contract_status">
<el-input <el-form-item :label="t('remarks')" >
v-model="formData.contract_status" <el-input v-model="formData.remarks" clearable :placeholder="t('remarksPlaceholder')" class="input-width" />
clearable </el-form-item>
:placeholder="t('contractStatusPlaceholder')"
class="input-width" </el-form>
/>
</el-form-item> <template #footer>
<span class="dialog-footer">
<el-form-item :label="t('contractType')" prop="contract_type"> <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-input <el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
v-model="formData.contract_type" t('confirm')
clearable }}</el-button>
:placeholder="t('contractTypePlaceholder')" </span>
class="input-width" </template>
/> </el-dialog>
</el-form-item>
<el-form-item :label="t('remarks')">
<el-input
v-model="formData.remarks"
clearable
:placeholder="t('remarksPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('createdAt')">
<el-input
v-model="formData.created_at"
clearable
:placeholder="t('createdAtPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('updatedAt')">
<el-input
v-model="formData.updated_at"
clearable
:placeholder="t('updatedAtPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('deletedAt')">
<el-input
v-model="formData.deleted_at"
clearable
:placeholder="t('deletedAtPlaceholder')"
class="input-width"
/>
</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> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -115,15 +64,12 @@ const loading = ref(false)
* 表单数据 * 表单数据
*/ */
const initialFormData = { const initialFormData = {
id: '', id: '',
contract_name: '', contract_name: '',
contract_template: '', contract_template: '',
contract_status: '', contract_status: '',
contract_type: '', contract_type: '',
remarks: '', remarks: '',
created_at: '',
updated_at: '',
deleted_at: '',
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -131,48 +77,33 @@ const formRef = ref<FormInstance>()
// //
const formRules = computed(() => { const formRules = computed(() => {
return { return {
contract_name: [ contract_name: [
{ { required: true, message: t('contractNamePlaceholder'), trigger: 'blur' },
required: true,
message: t('contractNamePlaceholder'), ]
trigger: 'blur', ,
},
],
contract_template: [ contract_template: [
{ { required: true, message: t('contractTemplatePlaceholder'), trigger: 'blur' },
required: true,
message: t('contractTemplatePlaceholder'), ]
trigger: 'blur', ,
},
],
contract_status: [ contract_status: [
{ { required: true, message: t('contractStatusPlaceholder'), trigger: 'blur' },
required: true,
message: t('contractStatusPlaceholder'), ]
trigger: 'blur', ,
},
],
contract_type: [ contract_type: [
{ { required: true, message: t('contractTypePlaceholder'), trigger: 'blur' },
required: true,
message: t('contractTypePlaceholder'), ]
trigger: 'blur', ,
},
],
remarks: [ remarks: [
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' }, { required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
],
created_at: [ ]
{ required: true, message: t('createdAtPlaceholder'), trigger: 'blur' }, ,
], }
updated_at: [
{ required: true, message: t('updatedAtPlaceholder'), trigger: 'blur' },
],
deleted_at: [
{ required: true, message: t('deletedAtPlaceholder'), trigger: 'blur' },
],
}
}) })
const emit = defineEmits(['complete']) const emit = defineEmits(['complete'])
@ -182,93 +113,98 @@ const emit = defineEmits(['complete'])
* @param formEl * @param formEl
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
let save = formData.id ? editContract : addContract let save = formData.id ? editContract : addContract
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
let data = formData let data = formData
save(data) save(data).then(res => {
.then((res) => { loading.value = false
loading.value = false showDialog.value = false
showDialog.value = false emit('complete')
emit('complete') }).catch(err => {
}) loading.value = false
.catch((err) => { })
loading.value = false }
}) })
}
})
} }
// //
let contract_statusList = ref([])
const contract_statusDictList = async () => {
contract_statusList.value = await (await useDictionary('ht_status')).data.dictionary
}
contract_statusDictList();
watch(() => contract_statusList.value, () => { formData.contract_status = contract_statusList.value[0].value })
let contract_typeList = ref([])
const contract_typeDictList = async () => {
contract_typeList.value = await (await useDictionary('ht_type')).data.dictionary
}
contract_typeDictList();
watch(() => contract_typeList.value, () => { formData.contract_type = contract_typeList.value[0].value })
const setFormData = async (row: any = null) => { const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if (row) { if(row){
const data = await (await getContractInfo(row.id)).data const data = await (await getContractInfo(row.id)).data
if (data) if (data) Object.keys(formData).forEach((key: string) => {
Object.keys(formData).forEach((key: string) => { if (data[key] != undefined) formData[key] = data[key]
if (data[key] != undefined) formData[key] = data[key] })
}) }
} loading.value = false
loading.value = false
} }
// //
const mobileVerify = (rule: any, value: any, callback: any) => { const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) { if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile'))) callback(new Error(t('generateMobile')))
} else { } else {
callback() callback()
} }
} }
// //
const idCardVerify = (rule: any, value: any, callback: any) => { const idCardVerify = (rule: any, value: any, callback: any) => {
if ( 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)) {
value && callback(new Error(t('generateIdCard')))
!/^[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( } else {
value callback()
) }
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
} }
// //
const emailVerify = (rule: any, value: any, callback: any) => { const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail'))) callback(new Error(t('generateEmail')))
} else { } else {
callback() callback()
} }
} }
// //
const numberVerify = (rule: any, value: any, callback: any) => { const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) { if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber'))) callback(new Error(t('generateNumber')))
} else { } else {
callback() callback()
} }
} }
defineExpose({ defineExpose({
showDialog, showDialog,
setFormData, setFormData
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></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;
} }
</style> </style>

386
admin/src/app/views/contract/contract.vue

@ -1,177 +1,97 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<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">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addContract') }} <el-button type="primary" @click="addEvent">
</el-button> {{ t('addContract') }}
</div> </el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="contractTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('contractStatus')" prop="contract_status">
:inline="true" <el-select class="w-[280px]" v-model="contractTable.searchParam.contract_status" clearable :placeholder="t('contractStatusPlaceholder')">
:model="contractTable.searchParam" <el-option label="全部" value=""></el-option>
ref="searchFormRef" <el-option
> v-for="(item, index) in contract_statusList"
<el-form-item :label="t('contractName')" prop="contract_name"> :key="index"
<el-input :label="item.name"
v-model="contractTable.searchParam.contract_name" :value="item.value"
:placeholder="t('contractNamePlaceholder')" />
/> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('contractTemplate')" prop="contract_template">
<el-input
v-model="contractTable.searchParam.contract_template" <el-form-item :label="t('contractType')" prop="contract_type">
:placeholder="t('contractTemplatePlaceholder')" <el-select class="w-[280px]" v-model="contractTable.searchParam.contract_type" clearable :placeholder="t('contractTypePlaceholder')">
/> <el-option label="全部" value=""></el-option>
</el-form-item> <el-option
<el-form-item :label="t('contractStatus')" prop="contract_status"> v-for="(item, index) in contract_typeList"
<el-input :key="index"
v-model="contractTable.searchParam.contract_status" :label="item.name"
:placeholder="t('contractStatusPlaceholder')" :value="item.value"
/> />
</el-form-item> </el-select>
<el-form-item :label="t('contractType')" prop="contract_type"> </el-form-item>
<el-input
v-model="contractTable.searchParam.contract_type" <el-form-item :label="t('createdAt')" prop="created_at">
:placeholder="t('contractTypePlaceholder')" <el-date-picker v-model="contractTable.searchParam.created_at" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
/> :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
</el-form-item> </el-form-item>
<el-form-item :label="t('remarks')" prop="remarks">
<el-input <el-form-item>
v-model="contractTable.searchParam.remarks" <el-button type="primary" @click="loadContractList()">{{ t('search') }}</el-button>
:placeholder="t('remarksPlaceholder')" <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
/> </el-form-item>
</el-form-item> </el-form>
<el-form-item :label="t('createdAt')" prop="created_at"> </el-card>
<el-input
v-model="contractTable.searchParam.created_at" <div class="mt-[10px]">
:placeholder="t('createdAtPlaceholder')" <el-table :data="contractTable.data" size="large" v-loading="contractTable.loading">
/> <template #empty>
</el-form-item> <span>{{ !contractTable.loading ? t('emptyData') : '' }}</span>
<el-form-item :label="t('updatedAt')" prop="updated_at"> </template>
<el-input <el-table-column prop="contract_name" :label="t('contractName')" min-width="120" :show-overflow-tooltip="true"/>
v-model="contractTable.searchParam.updated_at"
:placeholder="t('updatedAtPlaceholder')" <el-table-column :label="t('contractStatus')" min-width="180" align="center" :show-overflow-tooltip="true">
/> <template #default="{ row }">
</el-form-item> <div v-for="(item, index) in contract_statusList">
<el-form-item :label="t('deletedAt')" prop="deleted_at"> <div v-if="item.value == row.contract_status">{{ item.name }}</div>
<el-input </div>
v-model="contractTable.searchParam.deleted_at" </template>
:placeholder="t('deletedAtPlaceholder')" </el-table-column>
/>
</el-form-item> <el-table-column :label="t('contractType')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<el-form-item> <div v-for="(item, index) in contract_typeList">
<el-button type="primary" @click="loadContractList()">{{ <div v-if="item.value == row.contract_type">{{ item.name }}</div>
t('search') </div>
}}</el-button> </template>
<el-button @click="resetForm(searchFormRef)">{{ </el-table-column>
t('reset')
}}</el-button> <el-table-column prop="created_at" :label="t('createdAt')" min-width="120" :show-overflow-tooltip="true"/>
</el-form-item>
</el-form> <el-table-column :label="t('operation')" fixed="right" min-width="120">
</el-card> <template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<div class="mt-[10px]"> <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
<el-table </template>
:data="contractTable.data" </el-table-column>
size="large"
v-loading="contractTable.loading" </el-table>
> <div class="mt-[16px] flex justify-end">
<template #empty> <el-pagination v-model:current-page="contractTable.page" v-model:page-size="contractTable.limit"
<span>{{ !contractTable.loading ? t('emptyData') : '' }}</span> layout="total, sizes, prev, pager, next, jumper" :total="contractTable.total"
</template> @size-change="loadContractList()" @current-change="loadContractList" />
<el-table-column </div>
prop="contract_name" </div>
:label="t('contractName')"
min-width="120" <edit ref="editContractDialog" @complete="loadContractList" />
:show-overflow-tooltip="true" </el-card>
/> </div>
<el-table-column
prop="contract_template"
:label="t('contractTemplate')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="contract_status"
:label="t('contractStatus')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="contract_type"
:label="t('contractType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="remarks"
:label="t('remarks')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="created_at"
:label="t('createdAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="updated_at"
:label="t('updatedAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="deleted_at"
:label="t('deletedAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="contractTable.page"
v-model:page-size="contractTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="contractTable.total"
@size-change="loadContractList()"
@current-change="loadContractList"
/>
</div>
</div>
<edit ref="editContractDialog" @complete="loadContractList" />
</el-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -180,28 +100,23 @@ import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { getContractList, deleteContract } from '@/app/api/contract' import { getContractList, deleteContract } from '@/app/api/contract'
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/contract/components/contract-edit.vue' import Edit from '@/app/views/contract/components/contract-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;
let contractTable = reactive({ let contractTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam:{
contract_name: '', "contract_status":"",
contract_template: '', "contract_type":"",
contract_status: '', "created_at":[]
contract_type: '', }
remarks: '',
created_at: '',
updated_at: '',
deleted_at: '',
},
}) })
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
@ -210,26 +125,34 @@ const searchFormRef = ref<FormInstance>()
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
// //
const contract_statusList = ref([] as any[])
const contract_statusDictList = async () => {
contract_statusList.value = await (await useDictionary('ht_status')).data.dictionary
}
contract_statusDictList();
const contract_typeList = ref([] as any[])
const contract_typeDictList = async () => {
contract_typeList.value = await (await useDictionary('ht_type')).data.dictionary
}
contract_typeDictList();
/** /**
* 获取合同列表 * 获取合同列表
*/ */
const loadContractList = (page: number = 1) => { const loadContractList = (page: number = 1) => {
contractTable.loading = true contractTable.loading = true
contractTable.page = page contractTable.page = page
getContractList({ getContractList({
page: contractTable.page, page: contractTable.page,
limit: contractTable.limit, limit: contractTable.limit,
...contractTable.searchParam, ...contractTable.searchParam
}) }).then(res => {
.then((res) => { contractTable.loading = false
contractTable.loading = false contractTable.data = res.data.data
contractTable.data = res.data.data contractTable.total = res.data.total
contractTable.total = res.data.total }).catch(() => {
}) contractTable.loading = false
.catch(() => {
contractTable.loading = false
}) })
} }
loadContractList() loadContractList()
@ -240,8 +163,8 @@ const editContractDialog: Record<string, any> | null = ref(null)
* 添加合同 * 添加合同
*/ */
const addEvent = () => { const addEvent = () => {
editContractDialog.value.setFormData() editContractDialog.value.setFormData()
editContractDialog.value.showDialog = true editContractDialog.value.showDialog = true
} }
/** /**
@ -249,42 +172,45 @@ const addEvent = () => {
* @param data * @param data
*/ */
const editEvent = (data: any) => { const editEvent = (data: any) => {
editContractDialog.value.setFormData(data) editContractDialog.value.setFormData(data)
editContractDialog.value.showDialog = true editContractDialog.value.showDialog = true
} }
/** /**
* 删除合同 * 删除合同
*/ */
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('contractDeleteTips'), t('warning'), { ElMessageBox.confirm(t('contractDeleteTips'), t('warning'),
confirmButtonText: t('confirm'), {
cancelButtonText: t('cancel'), confirmButtonText: t('confirm'),
type: 'warning', cancelButtonText: t('cancel'),
}).then(() => { type: 'warning',
deleteContract(id) }
.then(() => { ).then(() => {
loadContractList() deleteContract(id).then(() => {
}) loadContractList()
.catch(() => {}) }).catch(() => {
}) })
})
} }
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
formEl.resetFields() formEl.resetFields()
loadContractList() loadContractList()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */ /* 多行超出隐藏 */
.multi-hidden { .multi-hidden {
word-break: break-all; word-break: break-all;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
</style> </style>

320
admin/src/app/views/exam_answers/components/exam-answers-edit.vue

@ -1,77 +1,67 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateExamAnswers') : t('addExamAnswers')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
v-model="showDialog" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
:title="formData.id ? t('updateExamAnswers') : t('addExamAnswers')" <el-form-item :label="t('campusId')" prop="campus_id">
width="50%" <el-select class="input-width" v-model="formData.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
class="diy-dialog-wrap" <el-option label="请选择" value=""></el-option>
:destroy-on-close="true" <el-option
> v-for="(item, index) in campusIdList"
<el-form :key="index"
:model="formData" :label="item['campus_name']"
label-width="120px" :value="item['id']"
ref="formRef" />
:rules="formRules" </el-select>
class="page-form" </el-form-item>
v-loading="loading"
> <el-form-item :label="t('userId')" prop="user_id">
<el-form-item :label="t('campusId')" prop="campus_id"> <el-select class="input-width" v-model="formData.user_id" clearable :placeholder="t('userIdPlaceholder')">
<el-input <el-option label="请选择" value=""></el-option>
v-model="formData.campus_id" <el-option
clearable v-for="(item, index) in userIdList"
:placeholder="t('campusIdPlaceholder')" :key="index"
class="input-width" :label="item['name']"
/> :value="item['id']"
</el-form-item> />
</el-select>
<el-form-item :label="t('userId')" prop="user_id"> </el-form-item>
<el-input
v-model="formData.user_id" <el-form-item :label="t('questionId')" prop="question_id">
clearable <el-select class="input-width" v-model="formData.question_id" clearable :placeholder="t('questionIdPlaceholder')">
:placeholder="t('userIdPlaceholder')" <el-option label="请选择" value=""></el-option>
class="input-width" <el-option
/> v-for="(item, index) in questionIdList"
</el-form-item> :key="index"
:label="item['title']"
<el-form-item :label="t('questionId')" prop="question_id"> :value="item['id']"
<el-input />
v-model="formData.question_id" </el-select>
clearable </el-form-item>
:placeholder="t('questionIdPlaceholder')"
class="input-width" <el-form-item :label="t('answer')" >
/> <el-input v-model="formData.answer" clearable :placeholder="t('answerPlaceholder')" class="input-width" />
</el-form-item> </el-form-item>
<el-form-item :label="t('answer')"> <el-form-item :label="t('isCorrect')" >
<el-input <el-radio-group v-model="formData.is_correct" :placeholder="t('isCorrectPlaceholder')">
v-model="formData.answer" <el-radio
clearable v-for="(item, index) in is_correctList"
:placeholder="t('answerPlaceholder')" :key="index" :label="item.value">
class="input-width" {{ item.name }}
/> </el-radio>
</el-form-item> </el-radio-group>
</el-form-item>
<el-form-item :label="t('isCorrect')">
<el-input </el-form>
v-model="formData.is_correct"
clearable <template #footer>
:placeholder="t('isCorrectPlaceholder')" <span class="dialog-footer">
class="input-width" <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
/> <el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
</el-form-item> t('confirm')
</el-form> }}</el-button>
</span>
<template #footer> </template>
<span class="dialog-footer"> </el-dialog>
<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> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -79,11 +69,7 @@ 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 { import { addExamAnswers, editExamAnswers, getExamAnswersInfo, getWithCampusList, getWithPersonnelList, getWithExamQuestionsList } from '@/app/api/exam_answers'
addExamAnswers,
editExamAnswers,
getExamAnswersInfo,
} from '@/app/api/exam_answers'
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
@ -92,12 +78,12 @@ const loading = ref(false)
* 表单数据 * 表单数据
*/ */
const initialFormData = { const initialFormData = {
id: '', id: '',
campus_id: '', campus_id: '',
user_id: '', user_id: '',
question_id: '', question_id: '',
answer: '', answer: '',
is_correct: '', is_correct: '',
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -105,23 +91,33 @@ const formRef = ref<FormInstance>()
// //
const formRules = computed(() => { const formRules = computed(() => {
return { return {
campus_id: [ campus_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
],
]
,
user_id: [ user_id: [
{ required: true, message: t('userIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('userIdPlaceholder'), trigger: 'blur' },
],
]
,
question_id: [ question_id: [
{ required: true, message: t('questionIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('questionIdPlaceholder'), trigger: 'blur' },
],
]
,
answer: [ answer: [
{ required: true, message: t('answerPlaceholder'), trigger: 'blur' }, { required: true, message: t('answerPlaceholder'), trigger: 'blur' },
],
]
,
is_correct: [ is_correct: [
{ required: true, message: t('isCorrectPlaceholder'), trigger: 'blur' }, { required: true, message: t('isCorrectPlaceholder'), trigger: 'blur' },
],
} ]
,
}
}) })
const emit = defineEmits(['complete']) const emit = defineEmits(['complete'])
@ -131,93 +127,107 @@ const emit = defineEmits(['complete'])
* @param formEl * @param formEl
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
let save = formData.id ? editExamAnswers : addExamAnswers let save = formData.id ? editExamAnswers : addExamAnswers
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
let data = formData let data = formData
save(data) save(data).then(res => {
.then((res) => { loading.value = false
loading.value = false showDialog.value = false
showDialog.value = false emit('complete')
emit('complete') }).catch(err => {
}) loading.value = false
.catch((err) => { })
loading.value = false }
}) })
}
})
} }
// //
let is_correctList = ref([])
const is_correctDictList = async () => {
is_correctList.value = await (await useDictionary('is_correct')).data.dictionary
}
is_correctDictList();
watch(() => is_correctList.value, () => { formData.is_correct = is_correctList.value[0].value })
const campusIdList = ref([] as any[])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const userIdList = ref([] as any[])
const setUserIdList = async () => {
userIdList.value = await (await getWithPersonnelList({})).data
}
setUserIdList()
const questionIdList = ref([] as any[])
const setQuestionIdList = async () => {
questionIdList.value = await (await getWithExamQuestionsList({})).data
}
setQuestionIdList()
const setFormData = async (row: any = null) => { const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if (row) { if(row){
const data = await (await getExamAnswersInfo(row.id)).data const data = await (await getExamAnswersInfo(row.id)).data
if (data) if (data) Object.keys(formData).forEach((key: string) => {
Object.keys(formData).forEach((key: string) => { if (data[key] != undefined) formData[key] = data[key]
if (data[key] != undefined) formData[key] = data[key] })
}) }
} loading.value = false
loading.value = false
} }
// //
const mobileVerify = (rule: any, value: any, callback: any) => { const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) { if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile'))) callback(new Error(t('generateMobile')))
} else { } else {
callback() callback()
} }
} }
// //
const idCardVerify = (rule: any, value: any, callback: any) => { const idCardVerify = (rule: any, value: any, callback: any) => {
if ( 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)) {
value && callback(new Error(t('generateIdCard')))
!/^[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( } else {
value callback()
) }
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
} }
// //
const emailVerify = (rule: any, value: any, callback: any) => { const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail'))) callback(new Error(t('generateEmail')))
} else { } else {
callback() callback()
} }
} }
// //
const numberVerify = (rule: any, value: any, callback: any) => { const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) { if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber'))) callback(new Error(t('generateNumber')))
} else { } else {
callback() callback()
} }
} }
defineExpose({ defineExpose({
showDialog, showDialog,
setFormData, setFormData
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></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;
} }
</style> </style>

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

@ -1,165 +1,99 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<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">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addExamAnswers') }} <el-button type="primary" @click="addEvent">
</el-button> {{ t('addExamAnswers') }}
</div> </el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="examAnswersTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('campusId')" prop="campus_id">
:inline="true" <el-select class="w-[280px]" v-model="examAnswersTable.searchParam.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
:model="examAnswersTable.searchParam" <el-option
ref="searchFormRef" v-for="(item, index) in campusIdList"
> :key="index"
<el-form-item :label="t('campusId')" prop="campus_id"> :label="item['campus_name']"
<el-input :value="item['id']"
v-model="examAnswersTable.searchParam.campus_id" />
:placeholder="t('campusIdPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('userId')" prop="user_id"> <el-form-item>
<el-input <el-button type="primary" @click="loadExamAnswersList()">{{ t('search') }}</el-button>
v-model="examAnswersTable.searchParam.user_id" <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
:placeholder="t('userIdPlaceholder')" </el-form-item>
/> </el-form>
</el-form-item> </el-card>
<el-form-item :label="t('questionId')" prop="question_id">
<el-input <div class="mt-[10px]">
v-model="examAnswersTable.searchParam.question_id" <el-table :data="examAnswersTable.data" size="large" v-loading="examAnswersTable.loading">
:placeholder="t('questionIdPlaceholder')" <template #empty>
/> <span>{{ !examAnswersTable.loading ? t('emptyData') : '' }}</span>
</el-form-item> </template>
<el-form-item :label="t('answer')" prop="answer"> <el-table-column prop="campus_id_name" :label="t('campusId')" min-width="120" :show-overflow-tooltip="true"/>
<el-input
v-model="examAnswersTable.searchParam.answer" <el-table-column prop="user_id_name" :label="t('userId')" min-width="120" :show-overflow-tooltip="true"/>
:placeholder="t('answerPlaceholder')"
/> <el-table-column prop="question_id_name" :label="t('questionId')" min-width="120" :show-overflow-tooltip="true"/>
</el-form-item>
<el-form-item :label="t('isCorrect')" prop="is_correct"> <el-table-column prop="answer" :label="t('answer')" min-width="120" :show-overflow-tooltip="true"/>
<el-input
v-model="examAnswersTable.searchParam.is_correct" <el-table-column :label="t('isCorrect')" min-width="180" align="center" :show-overflow-tooltip="true">
:placeholder="t('isCorrectPlaceholder')" <template #default="{ row }">
/> <div v-for="(item, index) in is_correctList">
</el-form-item> <div v-if="item.value == row.is_correct">{{ item.name }}</div>
</div>
<el-form-item> </template>
<el-button type="primary" @click="loadExamAnswersList()">{{ </el-table-column>
t('search')
}}</el-button> <el-table-column prop="created_at" :label="t('createdAt')" min-width="120" :show-overflow-tooltip="true"/>
<el-button @click="resetForm(searchFormRef)">{{
t('reset') <el-table-column :label="t('operation')" fixed="right" min-width="120">
}}</el-button> <template #default="{ row }">
</el-form-item> <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
</el-form> <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</el-card> </template>
</el-table-column>
<div class="mt-[10px]">
<el-table </el-table>
:data="examAnswersTable.data" <div class="mt-[16px] flex justify-end">
size="large" <el-pagination v-model:current-page="examAnswersTable.page" v-model:page-size="examAnswersTable.limit"
v-loading="examAnswersTable.loading" layout="total, sizes, prev, pager, next, jumper" :total="examAnswersTable.total"
> @size-change="loadExamAnswersList()" @current-change="loadExamAnswersList" />
<template #empty> </div>
<span>{{ !examAnswersTable.loading ? t('emptyData') : '' }}</span> </div>
</template>
<el-table-column <edit ref="editExamAnswersDialog" @complete="loadExamAnswersList" />
prop="campus_id" </el-card>
:label="t('campusId')" </div>
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="user_id"
:label="t('userId')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="question_id"
:label="t('questionId')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="answer"
:label="t('answer')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="is_correct"
:label="t('isCorrect')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="examAnswersTable.page"
v-model:page-size="examAnswersTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="examAnswersTable.total"
@size-change="loadExamAnswersList()"
@current-change="loadExamAnswersList"
/>
</div>
</div>
<edit ref="editExamAnswersDialog" @complete="loadExamAnswersList" />
</el-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
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 { getExamAnswersList, deleteExamAnswers } from '@/app/api/exam_answers' import { getExamAnswersList, deleteExamAnswers, getWithCampusList, getWithPersonnelList, getWithExamQuestionsList } from '@/app/api/exam_answers'
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/exam_answers/components/exam-answers-edit.vue' import Edit from '@/app/views/exam_answers/components/exam-answers-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;
let examAnswersTable = reactive({ let examAnswersTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam:{
campus_id: '', "campus_id":""
user_id: '', }
question_id: '',
answer: '',
is_correct: '',
},
}) })
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
@ -168,26 +102,29 @@ const searchFormRef = ref<FormInstance>()
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
// //
const is_correctList = ref([] as any[])
const is_correctDictList = async () => {
is_correctList.value = await (await useDictionary('is_correct')).data.dictionary
}
is_correctDictList();
/** /**
* 获取答题记录列表 * 获取答题记录列表
*/ */
const loadExamAnswersList = (page: number = 1) => { const loadExamAnswersList = (page: number = 1) => {
examAnswersTable.loading = true examAnswersTable.loading = true
examAnswersTable.page = page examAnswersTable.page = page
getExamAnswersList({ getExamAnswersList({
page: examAnswersTable.page, page: examAnswersTable.page,
limit: examAnswersTable.limit, limit: examAnswersTable.limit,
...examAnswersTable.searchParam, ...examAnswersTable.searchParam
}) }).then(res => {
.then((res) => { examAnswersTable.loading = false
examAnswersTable.loading = false examAnswersTable.data = res.data.data
examAnswersTable.data = res.data.data examAnswersTable.total = res.data.total
examAnswersTable.total = res.data.total }).catch(() => {
}) examAnswersTable.loading = false
.catch(() => {
examAnswersTable.loading = false
}) })
} }
loadExamAnswersList() loadExamAnswersList()
@ -198,8 +135,8 @@ const editExamAnswersDialog: Record<string, any> | null = ref(null)
* 添加答题记录 * 添加答题记录
*/ */
const addEvent = () => { const addEvent = () => {
editExamAnswersDialog.value.setFormData() editExamAnswersDialog.value.setFormData()
editExamAnswersDialog.value.showDialog = true editExamAnswersDialog.value.showDialog = true
} }
/** /**
@ -207,42 +144,60 @@ const addEvent = () => {
* @param data * @param data
*/ */
const editEvent = (data: any) => { const editEvent = (data: any) => {
editExamAnswersDialog.value.setFormData(data) editExamAnswersDialog.value.setFormData(data)
editExamAnswersDialog.value.showDialog = true editExamAnswersDialog.value.showDialog = true
} }
/** /**
* 删除答题记录 * 删除答题记录
*/ */
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('examAnswersDeleteTips'), t('warning'), { ElMessageBox.confirm(t('examAnswersDeleteTips'), t('warning'),
confirmButtonText: t('confirm'), {
cancelButtonText: t('cancel'), confirmButtonText: t('confirm'),
type: 'warning', cancelButtonText: t('cancel'),
}).then(() => { type: 'warning',
deleteExamAnswers(id) }
.then(() => { ).then(() => {
loadExamAnswersList() deleteExamAnswers(id).then(() => {
}) loadExamAnswersList()
.catch(() => {}) }).catch(() => {
}) })
})
} }
const campusIdList = ref([])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const userIdList = ref([])
const setUserIdList = async () => {
userIdList.value = await (await getWithPersonnelList({})).data
}
setUserIdList()
const questionIdList = ref([])
const setQuestionIdList = async () => {
questionIdList.value = await (await getWithExamQuestionsList({})).data
}
setQuestionIdList()
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
formEl.resetFields() formEl.resetFields()
loadExamAnswersList() loadExamAnswersList()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */ /* 多行超出隐藏 */
.multi-hidden { .multi-hidden {
word-break: break-all; word-break: break-all;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
</style> </style>

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

@ -1,205 +1,298 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateExamPapers') : t('addExamPapers')" width="50%"
v-model="showDialog" class="diy-dialog-wrap" :destroy-on-close="true">
:title="formData.id ? t('updateExamPapers') : t('addExamPapers')" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form"
width="50%" v-loading="loading">
class="diy-dialog-wrap"
:destroy-on-close="true" <el-form-item label="试卷标题">
> <el-input v-model="formData.title" class="input-width" />
<el-form </el-form-item>
:model="formData"
label-width="120px"
ref="formRef" <el-form-item :label="t('selectionMode')" prop="selection_mode">
:rules="formRules" <el-select class="input-width" v-model="formData.selection_mode" clearable
class="page-form" :placeholder="t('selectionModePlaceholder')">
v-loading="loading" <el-option label="请选择" value=""></el-option>
> <el-option v-for="(item, index) in selection_modeList" :key="index" :label="item.name"
<el-form-item :label="t('selectionMode')" prop="selection_mode"> :value="item.value" />
<el-input </el-select>
v-model="formData.selection_mode" </el-form-item>
clearable
:placeholder="t('selectionModePlaceholder')" <!-- <el-form-item :label="t('questionsIds')" >
class="input-width" <el-input v-model="formData.questions_ids" clearable :placeholder="t('questionsIdsPlaceholder')" class="input-width" />
/> </el-form-item> -->
</el-form-item>
<el-form-item :label="t('questionsIds')" v-if="formData.selection_mode == 'manual'">
<el-form-item :label="t('totalScore')" prop="total_score"> <div class="mt-[10px]">
<el-input <el-table ref="tableRef" :data="questionsList" style="width: 100%"
v-model="formData.total_score" @selection-change="handleSelectionChange" :loading="loadinga">
clearable <el-table-column type="selection" width="55" />
:placeholder="t('totalScorePlaceholder')" <el-table-column prop="title" label="题目标题" />
class="input-width"
/> <el-table-column label="题型" min-width="180" align="center" :show-overflow-tooltip="true">
</el-form-item> <template #default="{ row }">
<div v-for="(item, index) in question_typeList">
<el-form-item :label="t('passingScore')" prop="passing_score"> <div v-if="item.value == row.question_type">{{ item.name }}</div>
<el-input </div>
v-model="formData.passing_score" </template>
clearable </el-table-column>
:placeholder="t('passingScorePlaceholder')"
class="input-width" <el-table-column label="题干类型" min-width="180" align="center" :show-overflow-tooltip="true">
/> <template #default="{ row }">
</el-form-item> <div v-for="(item, index) in question_content_typeList">
</el-form> <div v-if="item.value == row.question_content_type">{{ item.name }}</div>
</div>
<template #footer> </template>
<span class="dialog-footer"> </el-table-column>
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button <el-table-column label="题干内容" width="100" align="left">
type="primary" <template #default="{ row }">
:loading="loading" <el-avatar v-if="row.question_content_type == 'image'"
@click="confirm(formRef)" :src="img(row.question_content)" />
>{{ t('confirm') }}</el-button <div v-if="row.question_content_type == 'text'">{{ row.question_content }}</div>
> </template>
</span> </el-table-column>
</template>
</el-dialog>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="fetchQuestions()" @current-change="fetchQuestions" />
</div>
</div>
</el-form-item>
<el-form-item :label="t('totalScore')" prop="total_score">
<el-input-number v-model="formData.total_score" clearable :placeholder="t('totalScorePlaceholder')"
class="input-width" :min="0" max="100" />
</el-form-item>
<el-form-item :label="t('passingScore')" prop="passing_score">
<el-input-number v-model="formData.passing_score" clearable :placeholder="t('passingScorePlaceholder')"
class="input-width" :min="0" max="100" />
</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> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue' import { ref, reactive, computed, watch ,nextTick } 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 { import { addExamPapers, editExamPapers, getExamPapersInfo } from '@/app/api/exam_papers'
addExamPapers, import { getExamQuestionsList } from '@/app/api/exam_questions'
editExamPapers,
getExamPapersInfo, let showDialog = ref(false)
} from '@/app/api/exam_papers' const loading = ref(false)
let showDialog = ref(false) /**
const loading = ref(false) * 表单数据
*/
/** const initialFormData = {
* 表单数据 id: '',
*/ title: '',
const initialFormData = { selection_mode: '',
id: '', questions_ids: '',
selection_mode: '', total_score: '',
total_score: '', passing_score: '',
passing_score: '',
} }
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>() //
const question_typeList = ref([] as any[])
// const question_typeDictList = async () => {
const formRules = computed(() => { question_typeList.value = await (await useDictionary('question_type')).data.dictionary
return { }
selection_mode: [ question_typeDictList();
{ const question_content_typeList = ref([] as any[])
required: true, const question_content_typeDictList = async () => {
message: t('selectionModePlaceholder'), question_content_typeList.value = await (await useDictionary('question_content_type')).data.dictionary
trigger: 'blur', }
}, question_content_typeDictList();
],
total_score: [
{ required: true, message: t('totalScorePlaceholder'), trigger: 'blur' }, const dialogVisible = ref(false)
],
passing_score: [ //
{ const questionsList = ref([])
required: true, const loadinga = ref(false)
message: t('passingScorePlaceholder'), const total = ref(0)
trigger: 'blur', const pageSize = 10
}, const currentPage = ref(1)
],
} const selectedQuestions = ref([])
}) const tableRef = ref()
const emit = defineEmits(['complete']) const fetchQuestions = async (page = 1) => {
loadinga.value = true
/** currentPage.value = page
* 确认 getExamQuestionsList({
* @param formEl page: page,
*/ limit: pageSize
const confirm = async (formEl: FormInstance | undefined) => { }).then(res => {
if (loading.value || !formEl) return questionsList.value = res.data.data
let save = formData.id ? editExamPapers : addExamPapers total.value = res.data.total
loadinga.value = false
await formEl.validate(async (valid) => {
if (valid) { }).catch(() => {
loading.value = true loadinga.value = false
})
let data = formData }
save(data) fetchQuestions()
.then((res) => {
loading.value = false const handleSelectionChange = (val) => {
showDialog.value = false selectedQuestions.value = val
emit('complete') }
})
.catch((err) => {
loading.value = false
}) const formData : Record<string, any> = reactive({ ...initialFormData })
}
}) const formRef = ref<FormInstance>()
}
//
// const formRules = computed(() => {
return {
const setFormData = async (row: any = null) => { selection_mode: [
Object.assign(formData, initialFormData) { required: true, message: t('selectionModePlaceholder'), trigger: 'blur' },
loading.value = true
if (row) { ]
const data = await (await getExamPapersInfo(row.id)).data }
if (data) })
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key] const emit = defineEmits(['complete'])
})
} /**
loading.value = false * 确认
} * @param formEl
*/
// const confirm = async (formEl : FormInstance | undefined) => {
const mobileVerify = (rule: any, value: any, callback: any) => { if (loading.value || !formEl) return
if (value && !/^1[3-9]\d{9}$/.test(value)) { let save = formData.id ? editExamPapers : addExamPapers
callback(new Error(t('generateMobile')))
} else { await formEl.validate(async (valid) => {
callback() if (valid) {
} loading.value = true
}
if (formData.selection_mode == 'manual') {
// const ids = selectedQuestions.value.map(item => item.id).join(',')
const idCardVerify = (rule: any, value: any, callback: any) => { formData.questions_ids = ids
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( let data = formData
value
) save(data).then(res => {
) { loading.value = false
callback(new Error(t('generateIdCard'))) showDialog.value = false
} else { emit('complete')
callback() }).catch(err => {
} loading.value = false
} })
}
// })
const emailVerify = (rule: any, value: any, callback: any) => { }
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail'))) //
} else { let selection_modeList = ref([])
callback() const selection_modeDictList = async () => {
} selection_modeList.value = await (await useDictionary('selection_mode')).data.dictionary
} }
selection_modeDictList();
// watch(() => selection_modeList.value, () => { formData.selection_mode = selection_modeList.value[0].value })
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else { const setFormData = async (row : any = null) => {
callback() Object.assign(formData, initialFormData)
} loading.value = true
} if (row) {
const data = await (await getExamPapersInfo(row.id)).data
defineExpose({ if (data) Object.keys(formData).forEach((key : string) => {
showDialog, if (data[key] != undefined) formData[key] = data[key]
setFormData, })
}) }
nextTick(() => {
const ids = formData.questions_ids?.split(',') || []
console.log(ids);
questionsList.value.forEach(row => {
if (ids.includes(String(row.id))) {
tableRef.value.toggleRowSelection(row)
}
})
})
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> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></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;
} }
</style> </style>

295
admin/src/app/views/exam_papers/exam_papers.vue

@ -1,112 +1,82 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<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">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addExamPapers') }} <el-button type="primary" @click="addEvent">
</el-button> {{ t('addExamPapers') }}
</div> </el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="examPapersTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('selectionMode')" prop="selection_mode">
:inline="true" <el-select class="w-[280px]" v-model="examPapersTable.searchParam.selection_mode" clearable :placeholder="t('selectionModePlaceholder')">
:model="examPapersTable.searchParam" <el-option label="全部" value=""></el-option>
ref="searchFormRef" <el-option
> v-for="(item, index) in selection_modeList"
<el-form-item :label="t('selectionMode')" prop="selection_mode"> :key="index"
<el-input :label="item.name"
v-model="examPapersTable.searchParam.selection_mode" :value="item.value"
:placeholder="t('selectionModePlaceholder')" />
/> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('totalScore')" prop="total_score">
<el-input <el-form-item :label="t('createdAt')" prop="created_at">
v-model="examPapersTable.searchParam.total_score" <el-date-picker v-model="examPapersTable.searchParam.created_at" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:placeholder="t('totalScorePlaceholder')" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('passingScore')" prop="passing_score"> <el-form-item>
<el-input <el-button type="primary" @click="loadExamPapersList()">{{ t('search') }}</el-button>
v-model="examPapersTable.searchParam.passing_score" <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
:placeholder="t('passingScorePlaceholder')" </el-form-item>
/> </el-form>
</el-form-item> </el-card>
<el-form-item> <div class="mt-[10px]">
<el-button type="primary" @click="loadExamPapersList()">{{ <el-table :data="examPapersTable.data" size="large" v-loading="examPapersTable.loading">
t('search') <template #empty>
}}</el-button> <span>{{ !examPapersTable.loading ? t('emptyData') : '' }}</span>
<el-button @click="resetForm(searchFormRef)">{{ </template>
t('reset')
}}</el-button> <el-table-column prop="title" label="试卷标题" />
</el-form-item>
</el-form>
</el-card> <el-table-column :label="t('selectionMode')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div class="mt-[10px]"> <div v-for="(item, index) in selection_modeList">
<el-table <div v-if="item.value == row.selection_mode">{{ item.name }}</div>
:data="examPapersTable.data" </div>
size="large" </template>
v-loading="examPapersTable.loading" </el-table-column>
>
<template #empty> <el-table-column prop="total_score" :label="t('totalScore')" min-width="120" :show-overflow-tooltip="true"/>
<span>{{ !examPapersTable.loading ? t('emptyData') : '' }}</span>
</template> <el-table-column prop="passing_score" :label="t('passingScore')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column
prop="selection_mode" <el-table-column prop="created_at" :label="t('createdAt')" min-width="120" :show-overflow-tooltip="true"/>
:label="t('selectionMode')"
min-width="120" <el-table-column :label="t('operation')" fixed="right" min-width="120">
:show-overflow-tooltip="true" <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>
<el-table-column </template>
prop="total_score" </el-table-column>
:label="t('totalScore')"
min-width="120" </el-table>
:show-overflow-tooltip="true" <div class="mt-[16px] flex justify-end">
/> <el-pagination v-model:current-page="examPapersTable.page" v-model:page-size="examPapersTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="examPapersTable.total"
<el-table-column @size-change="loadExamPapersList()" @current-change="loadExamPapersList" />
prop="passing_score" </div>
:label="t('passingScore')" </div>
min-width="120"
:show-overflow-tooltip="true" <edit ref="editExamPapersDialog" @complete="loadExamPapersList" />
/> </el-card>
</div>
<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="examPapersTable.page"
v-model:page-size="examPapersTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="examPapersTable.total"
@size-change="loadExamPapersList()"
@current-change="loadExamPapersList"
/>
</div>
</div>
<edit ref="editExamPapersDialog" @complete="loadExamPapersList" />
</el-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -115,23 +85,22 @@ import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { getExamPapersList, deleteExamPapers } from '@/app/api/exam_papers' import { getExamPapersList, deleteExamPapers } from '@/app/api/exam_papers'
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/exam_papers/components/exam-papers-edit.vue' import Edit from '@/app/views/exam_papers/components/exam-papers-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;
let examPapersTable = reactive({ let examPapersTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam:{
selection_mode: '', "selection_mode":"",
total_score: '', "created_at":[]
passing_score: '', }
},
}) })
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
@ -140,26 +109,29 @@ const searchFormRef = ref<FormInstance>()
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
// //
const selection_modeList = ref([] as any[])
const selection_modeDictList = async () => {
selection_modeList.value = await (await useDictionary('selection_mode')).data.dictionary
}
selection_modeDictList();
/** /**
* 获取试卷列表 * 获取试卷列表
*/ */
const loadExamPapersList = (page: number = 1) => { const loadExamPapersList = (page: number = 1) => {
examPapersTable.loading = true examPapersTable.loading = true
examPapersTable.page = page examPapersTable.page = page
getExamPapersList({ getExamPapersList({
page: examPapersTable.page, page: examPapersTable.page,
limit: examPapersTable.limit, limit: examPapersTable.limit,
...examPapersTable.searchParam, ...examPapersTable.searchParam
}) }).then(res => {
.then((res) => { examPapersTable.loading = false
examPapersTable.loading = false examPapersTable.data = res.data.data
examPapersTable.data = res.data.data examPapersTable.total = res.data.total
examPapersTable.total = res.data.total }).catch(() => {
}) examPapersTable.loading = false
.catch(() => {
examPapersTable.loading = false
}) })
} }
loadExamPapersList() loadExamPapersList()
@ -170,8 +142,8 @@ const editExamPapersDialog: Record<string, any> | null = ref(null)
* 添加试卷 * 添加试卷
*/ */
const addEvent = () => { const addEvent = () => {
editExamPapersDialog.value.setFormData() editExamPapersDialog.value.setFormData()
editExamPapersDialog.value.showDialog = true editExamPapersDialog.value.showDialog = true
} }
/** /**
@ -179,42 +151,45 @@ const addEvent = () => {
* @param data * @param data
*/ */
const editEvent = (data: any) => { const editEvent = (data: any) => {
editExamPapersDialog.value.setFormData(data) editExamPapersDialog.value.setFormData(data)
editExamPapersDialog.value.showDialog = true editExamPapersDialog.value.showDialog = true
} }
/** /**
* 删除试卷 * 删除试卷
*/ */
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('examPapersDeleteTips'), t('warning'), { ElMessageBox.confirm(t('examPapersDeleteTips'), t('warning'),
confirmButtonText: t('confirm'), {
cancelButtonText: t('cancel'), confirmButtonText: t('confirm'),
type: 'warning', cancelButtonText: t('cancel'),
}).then(() => { type: 'warning',
deleteExamPapers(id) }
.then(() => { ).then(() => {
loadExamPapersList() deleteExamPapers(id).then(() => {
}) loadExamPapersList()
.catch(() => {}) }).catch(() => {
}) })
})
} }
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
formEl.resetFields() formEl.resetFields()
loadExamPapersList() loadExamPapersList()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */ /* 多行超出隐藏 */
.multi-hidden { .multi-hidden {
word-break: break-all; word-break: break-all;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
</style> </style>

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

@ -0,0 +1,287 @@
<template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updateExamQuestions') : t('addExamQuestions')" width="50%"
class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form"
v-loading="loading">
<el-form-item :label="t('title')" prop="title">
<el-input v-model="formData.title" clearable :placeholder="t('titlePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('questionType')" prop="question_type">
<el-select class="input-width" v-model="formData.question_type" clearable
:placeholder="t('questionTypePlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option v-for="(item, index) in question_typeList" :key="index" :label="item.name"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('questionContentType')" prop="question_content_type">
<el-select class="input-width" v-model="formData.question_content_type" clearable
:placeholder="t('questionContentTypePlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option v-for="(item, index) in question_content_typeList" :key="index" :label="item.name"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('questionContent')" v-if="formData.question_content_type == 'text'">
<el-input v-model="formData.question_content" clearable class="input-width" />
</el-form-item>
<el-form-item :label="t('questionContent')" v-if="formData.question_content_type == 'image'">
<upload-image v-model="formData.question_content" />
</el-form-item>
<!-- <el-form-item :label="t('optionJson')" prop="option_json">
<el-input v-model="formData.option_json" clearable :placeholder="t('optionJsonPlaceholder')" class="input-width" />
</el-form-item> -->
<el-form-item :label="t('optionJson')">
<div v-for="(option, index) in formData.option_json" :key="index" class="option-item">
<el-card class="box-card" shadow="never">
<el-form-item label="类型" label-width="60">
<el-select v-model="option.option_content_type" placeholder="请选择类型">
<el-option label="文本" value="text" />
<el-option label="图片" value="image" />
</el-select>
</el-form-item>
<el-form-item label="内容" label-width="60" style="margin-top:5px;">
<template v-if="option.option_content_type === 'text'">
<el-input v-model="option.option_content" placeholder="请输入选项文本" />
</template>
<template v-else>
<upload-file v-model="option.option_content" />
</template>
</el-form-item>
<el-form-item label="答案" label-width="60">
<el-switch v-model="option.correct_answer"
:disabled="formData.question_type === 'single_choice' && option.correct_answer"
@change="handleCorrectAnswerChange(index)" />
</el-form-item>
<el-form-item label="操作" label-width="60">
<el-button @click="removeOption(index)" type="danger" size="small" plain>删除</el-button>
</el-form-item>
</el-card>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="addOption">添加选项</el-button>
</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 { addExamQuestions, editExamQuestions, getExamQuestionsInfo } from '@/app/api/exam_questions'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
title: '',
question_type: '',
question_content_type: '',
question_content: '',
option_json: []
}
const addOption = () => {
formData.option_json.push({
option_content_type: 'text',
option_content: '',
correct_answer: false
})
}
const removeOption = (index) => {
formData.option_json.splice(index, 1)
}
const handleCorrectAnswerChange = (changedIndex) => {
if (formData.question_type === 'single_choice') {
formData.options.forEach((opt, i) => {
if (i !== changedIndex) opt.correct_answer = false
})
}
}
const formData : Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
]
,
question_type: [
{ required: true, message: t('questionTypePlaceholder'), trigger: 'blur' },
]
,
question_content_type: [
{ required: true, message: t('questionContentTypePlaceholder'), trigger: 'blur' },
]
,
question_content: [
{ required: true, message: t('questionContentPlaceholder'), trigger: 'blur' },
]
,
option_json: [
{ required: true, message: t('optionJsonPlaceholder'), trigger: 'blur' },
]
,
correct_answer: [
{ required: true, message: t('correctAnswerPlaceholder'), trigger: 'blur' },
]
,
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl : FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editExamQuestions : addExamQuestions
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 question_typeList = ref([])
const question_typeDictList = async () => {
question_typeList.value = await (await useDictionary('question_type')).data.dictionary
}
question_typeDictList();
watch(() => question_typeList.value, () => { formData.question_type = question_typeList.value[0].value })
let question_content_typeList = ref([])
const question_content_typeDictList = async () => {
question_content_typeList.value = await (await useDictionary('question_content_type')).data.dictionary
}
question_content_typeDictList();
watch(() => question_content_typeList.value, () => { formData.question_content_type = question_content_typeList.value[0].value })
const setFormData = async (row : any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getExamQuestionsInfo(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;
}
.option-item {
margin-bottom: 20px;
margin-right: 10px;
}
</style>

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

@ -1,279 +1,122 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<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">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addExamQuestions') }} <el-button type="primary" @click="addEvent">
</el-button> {{ t('addExamQuestions') }}
</div> </el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="examQuestionsTable.searchParam" ref="searchFormRef">
> <el-form-item :label="t('title')" prop="title">
<el-form <el-input v-model="examQuestionsTable.searchParam.title" :placeholder="t('titlePlaceholder')" />
:inline="true" </el-form-item>
:model="examQuestionsTable.searchParam"
ref="searchFormRef" <el-form-item :label="t('questionType')" prop="question_type">
> <el-select class="w-[280px]" v-model="examQuestionsTable.searchParam.question_type" clearable :placeholder="t('questionTypePlaceholder')">
<el-form-item :label="t('questionType')" prop="question_type"> <el-option label="全部" value=""></el-option>
<el-input <el-option
v-model="examQuestionsTable.searchParam.question_type" v-for="(item, index) in question_typeList"
:placeholder="t('questionTypePlaceholder')" :key="index"
/> :label="item.name"
</el-form-item> :value="item.value"
<el-form-item />
:label="t('questionContentType')" </el-select>
prop="question_content_type" </el-form-item>
>
<el-input <el-form-item :label="t('createdAt')" prop="created_at">
v-model="examQuestionsTable.searchParam.question_content_type" <el-date-picker v-model="examQuestionsTable.searchParam.created_at" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:placeholder="t('questionContentTypePlaceholder')" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('questionContent')" prop="question_content"> <el-form-item>
<el-input <el-button type="primary" @click="loadExamQuestionsList()">{{ t('search') }}</el-button>
v-model="examQuestionsTable.searchParam.question_content" <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
:placeholder="t('questionContentPlaceholder')" </el-form-item>
/> </el-form>
</el-form-item> </el-card>
<el-form-item
:label="t('optionAContentType')" <div class="mt-[10px]">
prop="option_a_content_type" <el-table :data="examQuestionsTable.data" size="large" v-loading="examQuestionsTable.loading">
> <template #empty>
<el-input <span>{{ !examQuestionsTable.loading ? t('emptyData') : '' }}</span>
v-model="examQuestionsTable.searchParam.option_a_content_type" </template>
:placeholder="t('optionAContentTypePlaceholder')" <el-table-column prop="title" :label="t('title')" min-width="120" :show-overflow-tooltip="true"/>
/>
</el-form-item> <el-table-column :label="t('questionType')" min-width="180" align="center" :show-overflow-tooltip="true">
<el-form-item :label="t('optionAContent')" prop="option_a_content"> <template #default="{ row }">
<el-input <div v-for="(item, index) in question_typeList">
v-model="examQuestionsTable.searchParam.option_a_content" <div v-if="item.value == row.question_type">{{ item.name }}</div>
:placeholder="t('optionAContentPlaceholder')" </div>
/> </template>
</el-form-item> </el-table-column>
<el-form-item
:label="t('optionBContentType')" <el-table-column :label="t('questionContentType')" min-width="180" align="center" :show-overflow-tooltip="true">
prop="option_b_content_type" <template #default="{ row }">
> <div v-for="(item, index) in question_content_typeList">
<el-input <div v-if="item.value == row.question_content_type">{{ item.name }}</div>
v-model="examQuestionsTable.searchParam.option_b_content_type" </div>
:placeholder="t('optionBContentTypePlaceholder')" </template>
/> </el-table-column>
</el-form-item>
<el-form-item :label="t('optionBContent')" prop="option_b_content"> <el-table-column :label="t('questionContent')" width="100" align="left">
<el-input <template #default="{ row }">
v-model="examQuestionsTable.searchParam.option_b_content" <el-avatar v-if="row.question_content_type == 'image'" :src="img(row.question_content)" />
:placeholder="t('optionBContentPlaceholder')" <div v-if="row.question_content_type == 'text'">{{ row.question_content }}</div>
/> </template>
</el-form-item> </el-table-column>
<el-form-item
:label="t('optionCContentType')"
prop="option_c_content_type" <el-table-column prop="created_at" :label="t('createdAt')" min-width="120" :show-overflow-tooltip="true"/>
>
<el-input <el-table-column prop="updated_at" :label="t('updatedAt')" min-width="120" :show-overflow-tooltip="true"/>
v-model="examQuestionsTable.searchParam.option_c_content_type"
:placeholder="t('optionCContentTypePlaceholder')" <el-table-column :label="t('operation')" fixed="right" min-width="120">
/> <template #default="{ row }">
</el-form-item> <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-form-item :label="t('optionCContent')" prop="option_c_content"> <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
<el-input </template>
v-model="examQuestionsTable.searchParam.option_c_content" </el-table-column>
:placeholder="t('optionCContentPlaceholder')"
/> </el-table>
</el-form-item> <div class="mt-[16px] flex justify-end">
<el-form-item <el-pagination v-model:current-page="examQuestionsTable.page" v-model:page-size="examQuestionsTable.limit"
:label="t('optionDContentType')" layout="total, sizes, prev, pager, next, jumper" :total="examQuestionsTable.total"
prop="option_d_content_type" @size-change="loadExamQuestionsList()" @current-change="loadExamQuestionsList" />
> </div>
<el-input </div>
v-model="examQuestionsTable.searchParam.option_d_content_type"
:placeholder="t('optionDContentTypePlaceholder')" <edit ref="editExamQuestionsDialog" @complete="loadExamQuestionsList" />
/> </el-card>
</el-form-item> </div>
<el-form-item :label="t('optionDContent')" prop="option_d_content">
<el-input
v-model="examQuestionsTable.searchParam.option_d_content"
:placeholder="t('optionDContentPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('correctAnswer')" prop="correct_answer">
<el-input
v-model="examQuestionsTable.searchParam.correct_answer"
:placeholder="t('correctAnswerPlaceholder')"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadExamQuestionsList()">{{
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="examQuestionsTable.data"
size="large"
v-loading="examQuestionsTable.loading"
>
<template #empty>
<span>{{ !examQuestionsTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="question_type"
:label="t('questionType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="question_content_type"
:label="t('questionContentType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="question_content"
:label="t('questionContent')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="option_a_content_type"
:label="t('optionAContentType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="option_a_content"
:label="t('optionAContent')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="option_b_content_type"
:label="t('optionBContentType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="option_b_content"
:label="t('optionBContent')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="option_c_content_type"
:label="t('optionCContentType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="option_c_content"
:label="t('optionCContent')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="option_d_content_type"
:label="t('optionDContentType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="option_d_content"
:label="t('optionDContent')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="correct_answer"
:label="t('correctAnswer')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="examQuestionsTable.page"
v-model:page-size="examQuestionsTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="examQuestionsTable.total"
@size-change="loadExamQuestionsList()"
@current-change="loadExamQuestionsList"
/>
</div>
</div>
</el-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
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 { import { getExamQuestionsList, deleteExamQuestions } from '@/app/api/exam_questions'
getExamQuestionsList,
deleteExamQuestions,
} from '@/app/api/exam_questions'
import { img } from '@/utils/common' import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus' import { ElMessageBox,FormInstance } from 'element-plus'
import { useRouter } from 'vue-router' import Edit from '@/app/views/exam_questions/components/exam-questions-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;
let examQuestionsTable = reactive({ let examQuestionsTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam:{
question_type: '', "title":"",
question_content_type: '', "question_type":"",
question_content: '', "created_at":[]
option_a_content_type: '', }
option_a_content: '',
option_b_content_type: '',
option_b_content: '',
option_c_content_type: '',
option_c_content: '',
option_d_content_type: '',
option_d_content: '',
correct_answer: '',
},
}) })
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
@ -282,37 +125,46 @@ const searchFormRef = ref<FormInstance>()
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
// //
const question_typeList = ref([] as any[])
const question_typeDictList = async () => {
question_typeList.value = await (await useDictionary('question_type')).data.dictionary
}
question_typeDictList();
const question_content_typeList = ref([] as any[])
const question_content_typeDictList = async () => {
question_content_typeList.value = await (await useDictionary('question_content_type')).data.dictionary
}
question_content_typeDictList();
/** /**
* 获取试题列表 * 获取试题列表
*/ */
const loadExamQuestionsList = (page: number = 1) => { const loadExamQuestionsList = (page: number = 1) => {
examQuestionsTable.loading = true examQuestionsTable.loading = true
examQuestionsTable.page = page examQuestionsTable.page = page
getExamQuestionsList({ getExamQuestionsList({
page: examQuestionsTable.page, page: examQuestionsTable.page,
limit: examQuestionsTable.limit, limit: examQuestionsTable.limit,
...examQuestionsTable.searchParam, ...examQuestionsTable.searchParam
}) }).then(res => {
.then((res) => { examQuestionsTable.loading = false
examQuestionsTable.loading = false examQuestionsTable.data = res.data.data
examQuestionsTable.data = res.data.data examQuestionsTable.total = res.data.total
examQuestionsTable.total = res.data.total }).catch(() => {
}) examQuestionsTable.loading = false
.catch(() => {
examQuestionsTable.loading = false
}) })
} }
loadExamQuestionsList() loadExamQuestionsList()
const router = useRouter() const editExamQuestionsDialog: Record<string, any> | null = ref(null)
/** /**
* 添加试题 * 添加试题
*/ */
const addEvent = () => { const addEvent = () => {
router.push('/exam_questions/exam_questions_edit') editExamQuestionsDialog.value.setFormData()
editExamQuestionsDialog.value.showDialog = true
} }
/** /**
@ -320,41 +172,45 @@ const addEvent = () => {
* @param data * @param data
*/ */
const editEvent = (data: any) => { const editEvent = (data: any) => {
router.push('/exam_questions/exam_questions_edit?id=' + data.id) editExamQuestionsDialog.value.setFormData(data)
editExamQuestionsDialog.value.showDialog = true
} }
/** /**
* 删除试题 * 删除试题
*/ */
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('examQuestionsDeleteTips'), t('warning'), { ElMessageBox.confirm(t('examQuestionsDeleteTips'), t('warning'),
confirmButtonText: t('confirm'), {
cancelButtonText: t('cancel'), confirmButtonText: t('confirm'),
type: 'warning', cancelButtonText: t('cancel'),
}).then(() => { type: 'warning',
deleteExamQuestions(id) }
.then(() => { ).then(() => {
loadExamQuestionsList() deleteExamQuestions(id).then(() => {
}) loadExamQuestionsList()
.catch(() => {}) }).catch(() => {
}) })
})
} }
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
formEl.resetFields() formEl.resetFields()
loadExamQuestionsList() loadExamQuestionsList()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */ /* 多行超出隐藏 */
.multi-hidden { .multi-hidden {
word-break: break-all; word-break: break-all;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
</style> </style>

370
admin/src/app/views/exam_records/components/exam-records-edit.vue

@ -1,95 +1,89 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateExamRecords') : t('addExamRecords')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
v-model="showDialog" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
:title="formData.id ? t('updateExamRecords') : t('addExamRecords')" <el-form-item :label="t('campusId')" prop="campus_id">
width="50%" <el-select class="input-width" v-model="formData.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
class="diy-dialog-wrap" <el-option label="请选择" value=""></el-option>
:destroy-on-close="true" <el-option
> v-for="(item, index) in campusIdList"
<el-form :key="index"
:model="formData" :label="item['campus_name']"
label-width="120px" :value="item['id']"
ref="formRef" />
:rules="formRules" </el-select>
class="page-form" </el-form-item>
v-loading="loading"
> <el-form-item :label="t('userId')" prop="user_id">
<el-form-item :label="t('campusId')" prop="campus_id"> <el-select class="input-width" v-model="formData.user_id" clearable :placeholder="t('userIdPlaceholder')">
<el-input <el-option label="请选择" value=""></el-option>
v-model="formData.campus_id" <el-option
clearable v-for="(item, index) in userIdList"
:placeholder="t('campusIdPlaceholder')" :key="index"
class="input-width" :label="item['name']"
/> :value="item['id']"
</el-form-item> />
</el-select>
<el-form-item :label="t('userId')" prop="user_id"> </el-form-item>
<el-input
v-model="formData.user_id" <el-form-item :label="t('paperId')" prop="paper_id">
clearable <el-select class="input-width" v-model="formData.paper_id" clearable :placeholder="t('paperIdPlaceholder')">
:placeholder="t('userIdPlaceholder')" <el-option label="请选择" value=""></el-option>
class="input-width" <el-option
/> v-for="(item, index) in paperIdList"
</el-form-item> :key="index"
:label="item['title']"
<el-form-item :label="t('paperId')" prop="paper_id"> :value="item['id']"
<el-input />
v-model="formData.paper_id" </el-select>
clearable </el-form-item>
:placeholder="t('paperIdPlaceholder')"
class="input-width" <el-form-item :label="t('score')" >
/> <el-input-number v-model="formData.score" clearable :placeholder="t('scorePlaceholder')" class="input-width" :min = "0" max = "100" />
</el-form-item> </el-form-item>
<el-form-item :label="t('score')"> <el-form-item :label="t('status')" >
<el-input <el-select class="input-width" v-model="formData.status" clearable :placeholder="t('statusPlaceholder')">
v-model="formData.score" <el-option label="请选择" value=""></el-option>
clearable <el-option
:placeholder="t('scorePlaceholder')" v-for="(item, index) in statusList"
class="input-width" :key="index"
/> :label="item.name"
</el-form-item> :value="item.value"
/>
<el-form-item :label="t('status')"> </el-select>
<el-input </el-form-item>
v-model="formData.status"
clearable <el-form-item :label="t('startTime')" class="input-width">
:placeholder="t('statusPlaceholder')" <el-date-picker
class="input-width" class="flex-1 !flex"
/> v-model="formData.start_time"
</el-form-item> clearable
type="datetime"
<el-form-item :label="t('startTime')"> value-format="YYYY-MM-DD HH:mm:ss"
<el-input :placeholder="t('startTimePlaceholder')">
v-model="formData.start_time" </el-date-picker>
clearable </el-form-item>
:placeholder="t('startTimePlaceholder')" <el-form-item :label="t('endTime')" class="input-width">
class="input-width" <el-date-picker
/> class="flex-1 !flex"
</el-form-item> v-model="formData.end_time"
clearable
<el-form-item :label="t('endTime')"> type="datetime"
<el-input value-format="YYYY-MM-DD HH:mm:ss"
v-model="formData.end_time" :placeholder="t('endTimePlaceholder')">
clearable </el-date-picker>
:placeholder="t('endTimePlaceholder')" </el-form-item>
class="input-width" </el-form>
/>
</el-form-item> <template #footer>
</el-form> <span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<template #footer> <el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
<span class="dialog-footer"> t('confirm')
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button> }}</el-button>
<el-button </span>
type="primary" </template>
:loading="loading" </el-dialog>
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -97,11 +91,7 @@ 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 { import { addExamRecords, editExamRecords, getExamRecordsInfo, getWithCampusList, getWithPersonnelList, getWithExamPapersList } from '@/app/api/exam_records'
addExamRecords,
editExamRecords,
getExamRecordsInfo,
} from '@/app/api/exam_records'
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
@ -110,14 +100,14 @@ const loading = ref(false)
* 表单数据 * 表单数据
*/ */
const initialFormData = { const initialFormData = {
id: '', id: '',
campus_id: '', campus_id: '',
user_id: '', user_id: '',
paper_id: '', paper_id: '',
score: '', score: '',
status: '', status: '',
start_time: '', start_time: '',
end_time: '', end_time: '',
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -125,29 +115,43 @@ const formRef = ref<FormInstance>()
// //
const formRules = computed(() => { const formRules = computed(() => {
return { return {
campus_id: [ campus_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
],
]
,
user_id: [ user_id: [
{ required: true, message: t('userIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('userIdPlaceholder'), trigger: 'blur' },
],
]
,
paper_id: [ paper_id: [
{ required: true, message: t('paperIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('paperIdPlaceholder'), trigger: 'blur' },
],
]
,
score: [ score: [
{ required: true, message: t('scorePlaceholder'), trigger: 'blur' }, { required: true, message: t('scorePlaceholder'), trigger: 'blur' },
], { validator: (rule: any, value: string, callback: any) => { if (value && !/^\d{0,100}$/.test(value)) { callback(new Error(t('generateBetween')))} else { callback() }}},
]
,
status: [ status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' }, { required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
]
,
start_time: [ start_time: [
{ required: true, message: t('startTimePlaceholder'), trigger: 'blur' }, { required: true, message: t('startTimePlaceholder'), trigger: 'blur' },
],
]
,
end_time: [ end_time: [
{ required: true, message: t('endTimePlaceholder'), trigger: 'blur' }, { required: true, message: t('endTimePlaceholder'), trigger: 'blur' },
],
} ]
,
}
}) })
const emit = defineEmits(['complete']) const emit = defineEmits(['complete'])
@ -157,93 +161,107 @@ const emit = defineEmits(['complete'])
* @param formEl * @param formEl
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
let save = formData.id ? editExamRecords : addExamRecords let save = formData.id ? editExamRecords : addExamRecords
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
let data = formData let data = formData
save(data) save(data).then(res => {
.then((res) => { loading.value = false
loading.value = false showDialog.value = false
showDialog.value = false emit('complete')
emit('complete') }).catch(err => {
}) loading.value = false
.catch((err) => { })
loading.value = false }
}) })
}
})
} }
// //
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('ks_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const campusIdList = ref([] as any[])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const userIdList = ref([] as any[])
const setUserIdList = async () => {
userIdList.value = await (await getWithPersonnelList({})).data
}
setUserIdList()
const paperIdList = ref([] as any[])
const setPaperIdList = async () => {
paperIdList.value = await (await getWithExamPapersList({})).data
}
setPaperIdList()
const setFormData = async (row: any = null) => { const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if (row) { if(row){
const data = await (await getExamRecordsInfo(row.id)).data const data = await (await getExamRecordsInfo(row.id)).data
if (data) if (data) Object.keys(formData).forEach((key: string) => {
Object.keys(formData).forEach((key: string) => { if (data[key] != undefined) formData[key] = data[key]
if (data[key] != undefined) formData[key] = data[key] })
}) }
} loading.value = false
loading.value = false
} }
// //
const mobileVerify = (rule: any, value: any, callback: any) => { const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) { if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile'))) callback(new Error(t('generateMobile')))
} else { } else {
callback() callback()
} }
} }
// //
const idCardVerify = (rule: any, value: any, callback: any) => { const idCardVerify = (rule: any, value: any, callback: any) => {
if ( 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)) {
value && callback(new Error(t('generateIdCard')))
!/^[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( } else {
value callback()
) }
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
} }
// //
const emailVerify = (rule: any, value: any, callback: any) => { const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail'))) callback(new Error(t('generateEmail')))
} else { } else {
callback() callback()
} }
} }
// //
const numberVerify = (rule: any, value: any, callback: any) => { const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) { if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber'))) callback(new Error(t('generateNumber')))
} else { } else {
callback() callback()
} }
} }
defineExpose({ defineExpose({
showDialog, showDialog,
setFormData, setFormData
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></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;
} }
</style> </style>

390
admin/src/app/views/exam_records/exam_records.vue

@ -1,193 +1,128 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<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">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addExamRecords') }} <el-button type="primary" @click="addEvent">
</el-button> {{ t('addExamRecords') }}
</div> </el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="examRecordsTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('campusId')" prop="campus_id">
:inline="true" <el-select class="w-[280px]" v-model="examRecordsTable.searchParam.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
:model="examRecordsTable.searchParam" <el-option
ref="searchFormRef" v-for="(item, index) in campusIdList"
> :key="index"
<el-form-item :label="t('campusId')" prop="campus_id"> :label="item['campus_name']"
<el-input :value="item['id']"
v-model="examRecordsTable.searchParam.campus_id" />
:placeholder="t('campusIdPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('userId')" prop="user_id">
<el-input <el-form-item :label="t('paperId')" prop="paper_id">
v-model="examRecordsTable.searchParam.user_id" <el-select class="w-[280px]" v-model="examRecordsTable.searchParam.paper_id" clearable :placeholder="t('paperIdPlaceholder')">
:placeholder="t('userIdPlaceholder')" <el-option
/> v-for="(item, index) in paperIdList"
</el-form-item> :key="index"
<el-form-item :label="t('paperId')" prop="paper_id"> :label="item['title']"
<el-input :value="item['id']"
v-model="examRecordsTable.searchParam.paper_id" />
:placeholder="t('paperIdPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('score')" prop="score">
<el-input <el-form-item :label="t('status')" prop="status">
v-model="examRecordsTable.searchParam.score" <el-select class="w-[280px]" v-model="examRecordsTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
:placeholder="t('scorePlaceholder')" <el-option label="全部" value=""></el-option>
/> <el-option
</el-form-item> v-for="(item, index) in statusList"
<el-form-item :label="t('status')" prop="status"> :key="index"
<el-input :label="item.name"
v-model="examRecordsTable.searchParam.status" :value="item.value"
:placeholder="t('statusPlaceholder')" />
/> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('startTime')" prop="start_time">
<el-input <el-form-item>
v-model="examRecordsTable.searchParam.start_time" <el-button type="primary" @click="loadExamRecordsList()">{{ t('search') }}</el-button>
:placeholder="t('startTimePlaceholder')" <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
/> </el-form-item>
</el-form-item> </el-form>
<el-form-item :label="t('endTime')" prop="end_time"> </el-card>
<el-input
v-model="examRecordsTable.searchParam.end_time" <div class="mt-[10px]">
:placeholder="t('endTimePlaceholder')" <el-table :data="examRecordsTable.data" size="large" v-loading="examRecordsTable.loading">
/> <template #empty>
</el-form-item> <span>{{ !examRecordsTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-form-item> <el-table-column prop="campus_id_name" :label="t('campusId')" min-width="120" :show-overflow-tooltip="true"/>
<el-button type="primary" @click="loadExamRecordsList()">{{
t('search') <el-table-column prop="user_id_name" :label="t('userId')" min-width="120" :show-overflow-tooltip="true"/>
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ <el-table-column prop="paper_id_name" :label="t('paperId')" min-width="120" :show-overflow-tooltip="true"/>
t('reset')
}}</el-button> <el-table-column prop="score" :label="t('score')" min-width="120" :show-overflow-tooltip="true"/>
</el-form-item>
</el-form> <el-table-column :label="t('status')" min-width="180" align="center" :show-overflow-tooltip="true">
</el-card> <template #default="{ row }">
<div v-for="(item, index) in statusList">
<div class="mt-[10px]"> <div v-if="item.value == row.status">{{ item.name }}</div>
<el-table </div>
:data="examRecordsTable.data" </template>
size="large" </el-table-column>
v-loading="examRecordsTable.loading"
> <el-table-column prop="start_time" :label="t('startTime')" min-width="120" :show-overflow-tooltip="true"/>
<template #empty>
<span>{{ !examRecordsTable.loading ? t('emptyData') : '' }}</span> <el-table-column prop="end_time" :label="t('endTime')" min-width="120" :show-overflow-tooltip="true"/>
</template>
<el-table-column <el-table-column :label="t('operation')" fixed="right" min-width="120">
prop="campus_id" <template #default="{ row }">
:label="t('campusId')" <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
min-width="120" <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
:show-overflow-tooltip="true" </template>
/> </el-table-column>
<el-table-column </el-table>
prop="user_id" <div class="mt-[16px] flex justify-end">
:label="t('userId')" <el-pagination v-model:current-page="examRecordsTable.page" v-model:page-size="examRecordsTable.limit"
min-width="120" layout="total, sizes, prev, pager, next, jumper" :total="examRecordsTable.total"
:show-overflow-tooltip="true" @size-change="loadExamRecordsList()" @current-change="loadExamRecordsList" />
/> </div>
</div>
<el-table-column
prop="paper_id" <edit ref="editExamRecordsDialog" @complete="loadExamRecordsList" />
:label="t('paperId')" </el-card>
min-width="120" </div>
:show-overflow-tooltip="true"
/>
<el-table-column
prop="score"
:label="t('score')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="status"
:label="t('status')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="start_time"
:label="t('startTime')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="end_time"
:label="t('endTime')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="examRecordsTable.page"
v-model:page-size="examRecordsTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="examRecordsTable.total"
@size-change="loadExamRecordsList()"
@current-change="loadExamRecordsList"
/>
</div>
</div>
<edit ref="editExamRecordsDialog" @complete="loadExamRecordsList" />
</el-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
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 { getExamRecordsList, deleteExamRecords } from '@/app/api/exam_records' import { getExamRecordsList, deleteExamRecords, getWithCampusList, getWithPersonnelList, getWithExamPapersList } from '@/app/api/exam_records'
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/exam_records/components/exam-records-edit.vue' import Edit from '@/app/views/exam_records/components/exam-records-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;
let examRecordsTable = reactive({ let examRecordsTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam:{
campus_id: '', "campus_id":"",
user_id: '', "paper_id":"",
paper_id: '', "status":""
score: '', }
status: '',
start_time: '',
end_time: '',
},
}) })
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
@ -196,26 +131,29 @@ const searchFormRef = ref<FormInstance>()
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
// //
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('ks_status')).data.dictionary
}
statusDictList();
/** /**
* 获取考试记录列表 * 获取考试记录列表
*/ */
const loadExamRecordsList = (page: number = 1) => { const loadExamRecordsList = (page: number = 1) => {
examRecordsTable.loading = true examRecordsTable.loading = true
examRecordsTable.page = page examRecordsTable.page = page
getExamRecordsList({ getExamRecordsList({
page: examRecordsTable.page, page: examRecordsTable.page,
limit: examRecordsTable.limit, limit: examRecordsTable.limit,
...examRecordsTable.searchParam, ...examRecordsTable.searchParam
}) }).then(res => {
.then((res) => { examRecordsTable.loading = false
examRecordsTable.loading = false examRecordsTable.data = res.data.data
examRecordsTable.data = res.data.data examRecordsTable.total = res.data.total
examRecordsTable.total = res.data.total }).catch(() => {
}) examRecordsTable.loading = false
.catch(() => {
examRecordsTable.loading = false
}) })
} }
loadExamRecordsList() loadExamRecordsList()
@ -226,8 +164,8 @@ const editExamRecordsDialog: Record<string, any> | null = ref(null)
* 添加考试记录 * 添加考试记录
*/ */
const addEvent = () => { const addEvent = () => {
editExamRecordsDialog.value.setFormData() editExamRecordsDialog.value.setFormData()
editExamRecordsDialog.value.showDialog = true editExamRecordsDialog.value.showDialog = true
} }
/** /**
@ -235,42 +173,60 @@ const addEvent = () => {
* @param data * @param data
*/ */
const editEvent = (data: any) => { const editEvent = (data: any) => {
editExamRecordsDialog.value.setFormData(data) editExamRecordsDialog.value.setFormData(data)
editExamRecordsDialog.value.showDialog = true editExamRecordsDialog.value.showDialog = true
} }
/** /**
* 删除考试记录 * 删除考试记录
*/ */
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('examRecordsDeleteTips'), t('warning'), { ElMessageBox.confirm(t('examRecordsDeleteTips'), t('warning'),
confirmButtonText: t('confirm'), {
cancelButtonText: t('cancel'), confirmButtonText: t('confirm'),
type: 'warning', cancelButtonText: t('cancel'),
}).then(() => { type: 'warning',
deleteExamRecords(id) }
.then(() => { ).then(() => {
loadExamRecordsList() deleteExamRecords(id).then(() => {
}) loadExamRecordsList()
.catch(() => {}) }).catch(() => {
}) })
})
} }
const campusIdList = ref([])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const userIdList = ref([])
const setUserIdList = async () => {
userIdList.value = await (await getWithPersonnelList({})).data
}
setUserIdList()
const paperIdList = ref([])
const setPaperIdList = async () => {
paperIdList.value = await (await getWithExamPapersList({})).data
}
setPaperIdList()
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
formEl.resetFields() formEl.resetFields()
loadExamRecordsList() loadExamRecordsList()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */ /* 多行超出隐藏 */
.multi-hidden { .multi-hidden {
word-break: break-all; word-break: break-all;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
</style> </style>

372
admin/src/app/views/performance_records/components/performance-records-edit.vue

@ -1,97 +1,73 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updatePerformanceRecords') : t('addPerformanceRecords')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
v-model="showDialog" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
:title=" <el-form-item :label="t('staffId')" prop="staff_id">
formData.id ? t('updatePerformanceRecords') : t('addPerformanceRecords') <el-select class="input-width" v-model="formData.staff_id" clearable :placeholder="t('staffIdPlaceholder')">
" <el-option label="请选择" value=""></el-option>
width="50%" <el-option
class="diy-dialog-wrap" v-for="(item, index) in staffIdList"
:destroy-on-close="true" :key="index"
> :label="item['name']"
<el-form :value="item['id']"
:model="formData" />
label-width="120px" </el-select>
ref="formRef" </el-form-item>
:rules="formRules"
class="page-form" <el-form-item :label="t('resourceId')" prop="resource_id">
v-loading="loading" <el-select class="input-width" v-model="formData.resource_id" clearable :placeholder="t('resourceIdPlaceholder')">
> <el-option label="请选择" value=""></el-option>
<el-form-item :label="t('staffId')" prop="staff_id"> <el-option
<el-input v-for="(item, index) in resourceIdList"
v-model="formData.staff_id" :key="index"
clearable :label="item['name']"
:placeholder="t('staffIdPlaceholder')" :value="item['id']"
class="input-width" />
/> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('resourceId')" prop="resource_id"> <el-form-item :label="t('orderStatus')" prop="order_status">
<el-input <el-select class="input-width" v-model="formData.order_status" clearable :placeholder="t('orderStatusPlaceholder')">
v-model="formData.resource_id" <el-option label="请选择" value=""></el-option>
clearable <el-option
:placeholder="t('resourceIdPlaceholder')" v-for="(item, index) in order_statusList"
class="input-width" :key="index"
/> :label="item.name"
</el-form-item> :value="item.value"
/>
<el-form-item :label="t('orderId')"> </el-select>
<el-input </el-form-item>
v-model="formData.order_id"
clearable <el-form-item :label="t('performanceType')" prop="performance_type">
:placeholder="t('orderIdPlaceholder')" <el-select class="input-width" v-model="formData.performance_type" clearable :placeholder="t('performanceTypePlaceholder')">
class="input-width" <el-option label="请选择" value=""></el-option>
/> <el-option
</el-form-item> v-for="(item, index) in performance_typeList"
:key="index"
<el-form-item :label="t('orderStatus')"> :label="item.name"
<el-input :value="item.value"
v-model="formData.order_status" />
clearable </el-select>
:placeholder="t('orderStatusPlaceholder')" </el-form-item>
class="input-width"
/> <el-form-item :label="t('performanceValue')" prop="performance_value">
</el-form-item> <el-input v-model="formData.performance_value" clearable :placeholder="t('performanceValuePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('performanceType')" prop="performance_type">
<el-input <el-form-item :label="t('remarks')" >
v-model="formData.performance_type" <el-input v-model="formData.remarks" clearable :placeholder="t('remarksPlaceholder')" class="input-width" />
clearable </el-form-item>
:placeholder="t('performanceTypePlaceholder')"
class="input-width" </el-form>
/>
</el-form-item> <template #footer>
<span class="dialog-footer">
<el-form-item :label="t('performanceValue')" prop="performance_value"> <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-input <el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
v-model="formData.performance_value" t('confirm')
clearable }}</el-button>
:placeholder="t('performanceValuePlaceholder')" </span>
class="input-width" </template>
/> </el-dialog>
</el-form-item>
<el-form-item :label="t('remarks')">
<el-input
v-model="formData.remarks"
clearable
:placeholder="t('remarksPlaceholder')"
class="input-width"
/>
</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> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -99,11 +75,7 @@ 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 { import { addPerformanceRecords, editPerformanceRecords, getPerformanceRecordsInfo, getWithPersonnelList, getWithCustomerResourcesList, getWithOrderTableList } from '@/app/api/performance_records'
addPerformanceRecords,
editPerformanceRecords,
getPerformanceRecordsInfo,
} from '@/app/api/performance_records'
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
@ -112,14 +84,13 @@ const loading = ref(false)
* 表单数据 * 表单数据
*/ */
const initialFormData = { const initialFormData = {
id: '', id: '',
staff_id: '', staff_id: '',
resource_id: '', resource_id: '',
order_id: '', order_status: '',
order_status: '', performance_type: '',
performance_type: '', performance_value: '',
performance_value: '', remarks: '',
remarks: '',
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -127,37 +98,38 @@ const formRef = ref<FormInstance>()
// //
const formRules = computed(() => { const formRules = computed(() => {
return { return {
staff_id: [ staff_id: [
{ required: true, message: t('staffIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('staffIdPlaceholder'), trigger: 'blur' },
],
]
,
resource_id: [ resource_id: [
{ required: true, message: t('resourceIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('resourceIdPlaceholder'), trigger: 'blur' },
],
order_id: [ ]
{ required: true, message: t('orderIdPlaceholder'), trigger: 'blur' }, ,
],
order_status: [ order_status: [
{ required: true, message: t('orderStatusPlaceholder'), trigger: 'blur' }, { required: true, message: t('orderStatusPlaceholder'), trigger: 'blur' },
],
]
,
performance_type: [ performance_type: [
{ { required: true, message: t('performanceTypePlaceholder'), trigger: 'blur' },
required: true,
message: t('performanceTypePlaceholder'), ]
trigger: 'blur', ,
},
],
performance_value: [ performance_value: [
{ { required: true, message: t('performanceValuePlaceholder'), trigger: 'blur' },
required: true,
message: t('performanceValuePlaceholder'), ]
trigger: 'blur', ,
},
],
remarks: [ remarks: [
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' }, { required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
],
} ]
,
}
}) })
const emit = defineEmits(['complete']) const emit = defineEmits(['complete'])
@ -167,93 +139,113 @@ const emit = defineEmits(['complete'])
* @param formEl * @param formEl
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
let save = formData.id ? editPerformanceRecords : addPerformanceRecords let save = formData.id ? editPerformanceRecords : addPerformanceRecords
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
let data = formData let data = formData
save(data) save(data).then(res => {
.then((res) => { loading.value = false
loading.value = false showDialog.value = false
showDialog.value = false emit('complete')
emit('complete') }).catch(err => {
}) loading.value = false
.catch((err) => { })
loading.value = false }
}) })
}
})
} }
// //
let order_statusList = ref([])
const order_statusDictList = async () => {
order_statusList.value = await (await useDictionary('orderstatus')).data.dictionary
}
order_statusDictList();
watch(() => order_statusList.value, () => { formData.order_status = order_statusList.value[0].value })
let performance_typeList = ref([])
const performance_typeDictList = async () => {
performance_typeList.value = await (await useDictionary('performance_type')).data.dictionary
}
performance_typeDictList();
watch(() => performance_typeList.value, () => { formData.performance_type = performance_typeList.value[0].value })
const staffIdList = ref([] as any[])
const setStaffIdList = async () => {
staffIdList.value = await (await getWithPersonnelList({})).data
}
setStaffIdList()
const resourceIdList = ref([] as any[])
const setResourceIdList = async () => {
resourceIdList.value = await (await getWithCustomerResourcesList({})).data
}
setResourceIdList()
const orderIdList = ref([] as any[])
const setOrderIdList = async () => {
orderIdList.value = await (await getWithOrderTableList({})).data
}
setOrderIdList()
const setFormData = async (row: any = null) => { const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if (row) { if(row){
const data = await (await getPerformanceRecordsInfo(row.id)).data const data = await (await getPerformanceRecordsInfo(row.id)).data
if (data) if (data) Object.keys(formData).forEach((key: string) => {
Object.keys(formData).forEach((key: string) => { if (data[key] != undefined) formData[key] = data[key]
if (data[key] != undefined) formData[key] = data[key] })
}) }
} loading.value = false
loading.value = false
} }
// //
const mobileVerify = (rule: any, value: any, callback: any) => { const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) { if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile'))) callback(new Error(t('generateMobile')))
} else { } else {
callback() callback()
} }
} }
// //
const idCardVerify = (rule: any, value: any, callback: any) => { const idCardVerify = (rule: any, value: any, callback: any) => {
if ( 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)) {
value && callback(new Error(t('generateIdCard')))
!/^[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( } else {
value callback()
) }
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
} }
// //
const emailVerify = (rule: any, value: any, callback: any) => { const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail'))) callback(new Error(t('generateEmail')))
} else { } else {
callback() callback()
} }
} }
// //
const numberVerify = (rule: any, value: any, callback: any) => { const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) { if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber'))) callback(new Error(t('generateNumber')))
} else { } else {
callback() callback()
} }
} }
defineExpose({ defineExpose({
showDialog, showDialog,
setFormData, setFormData
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></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;
} }
</style> </style>

402
admin/src/app/views/performance_records/performance_records.vue

@ -1,201 +1,127 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<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">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addPerformanceRecords') }} <!-- <el-button type="primary" @click="addEvent">
</el-button> {{ t('addPerformanceRecords') }}
</div> </el-button> -->
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="performanceRecordsTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('staffId')" prop="staff_id">
:inline="true" <el-select class="w-[280px]" v-model="performanceRecordsTable.searchParam.staff_id" clearable :placeholder="t('staffIdPlaceholder')">
:model="performanceRecordsTable.searchParam" <el-option
ref="searchFormRef" v-for="(item, index) in staffIdList"
> :key="index"
<el-form-item :label="t('staffId')" prop="staff_id"> :label="item['name']"
<el-input :value="item['id']"
v-model="performanceRecordsTable.searchParam.staff_id" />
:placeholder="t('staffIdPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('resourceId')" prop="resource_id">
<el-input <el-form-item :label="t('orderStatus')" prop="order_status">
v-model="performanceRecordsTable.searchParam.resource_id" <el-select class="w-[280px]" v-model="performanceRecordsTable.searchParam.order_status" clearable :placeholder="t('orderStatusPlaceholder')">
:placeholder="t('resourceIdPlaceholder')" <el-option label="全部" value=""></el-option>
/> <el-option
</el-form-item> v-for="(item, index) in order_statusList"
<el-form-item :label="t('orderId')" prop="order_id"> :key="index"
<el-input :label="item.name"
v-model="performanceRecordsTable.searchParam.order_id" :value="item.value"
:placeholder="t('orderIdPlaceholder')" />
/> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('orderStatus')" prop="order_status">
<el-input <el-form-item :label="t('createdAt')" prop="created_at">
v-model="performanceRecordsTable.searchParam.order_status" <el-date-picker v-model="performanceRecordsTable.searchParam.created_at" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:placeholder="t('orderStatusPlaceholder')" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('performanceType')" prop="performance_type"> <el-form-item>
<el-input <el-button type="primary" @click="loadPerformanceRecordsList()">{{ t('search') }}</el-button>
v-model="performanceRecordsTable.searchParam.performance_type" <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
:placeholder="t('performanceTypePlaceholder')" </el-form-item>
/> </el-form>
</el-form-item> </el-card>
<el-form-item :label="t('performanceValue')" prop="performance_value">
<el-input <div class="mt-[10px]">
v-model="performanceRecordsTable.searchParam.performance_value" <el-table :data="performanceRecordsTable.data" size="large" v-loading="performanceRecordsTable.loading">
:placeholder="t('performanceValuePlaceholder')" <template #empty>
/> <span>{{ !performanceRecordsTable.loading ? t('emptyData') : '' }}</span>
</el-form-item> </template>
<el-form-item :label="t('remarks')" prop="remarks"> <el-table-column prop="staff_id_name" :label="t('staffId')" min-width="120" :show-overflow-tooltip="true"/>
<el-input
v-model="performanceRecordsTable.searchParam.remarks" <el-table-column prop="resource_id_name" :label="t('resourceId')" min-width="120" :show-overflow-tooltip="true"/>
:placeholder="t('remarksPlaceholder')"
/> <el-table-column :label="t('orderStatus')" min-width="180" align="center" :show-overflow-tooltip="true">
</el-form-item> <template #default="{ row }">
<div v-for="(item, index) in order_statusList">
<el-form-item> <div v-if="item.value == row.order_status">{{ item.name }}</div>
<el-button type="primary" @click="loadPerformanceRecordsList()">{{ </div>
t('search') </template>
}}</el-button> </el-table-column>
<el-button @click="resetForm(searchFormRef)">{{
t('reset') <el-table-column :label="t('performanceType')" min-width="180" align="center" :show-overflow-tooltip="true">
}}</el-button> <template #default="{ row }">
</el-form-item> <div v-for="(item, index) in performance_typeList">
</el-form> <div v-if="item.value == row.performance_type">{{ item.name }}</div>
</el-card> </div>
</template>
<div class="mt-[10px]"> </el-table-column>
<el-table
:data="performanceRecordsTable.data" <el-table-column prop="performance_value" :label="t('performanceValue')" min-width="120" :show-overflow-tooltip="true"/>
size="large"
v-loading="performanceRecordsTable.loading" <el-table-column prop="remarks" :label="t('remarks')" min-width="120" :show-overflow-tooltip="true"/>
>
<template #empty> <el-table-column prop="created_at" :label="t('createdAt')" min-width="120" :show-overflow-tooltip="true"/>
<span>{{
!performanceRecordsTable.loading ? t('emptyData') : '' <!-- <el-table-column :label="t('operation')" fixed="right" min-width="120">
}}</span> <template #default="{ row }">
</template> <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-table-column <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
prop="staff_id" </template>
:label="t('staffId')" </el-table-column> -->
min-width="120"
:show-overflow-tooltip="true" </el-table>
/> <div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="performanceRecordsTable.page" v-model:page-size="performanceRecordsTable.limit"
<el-table-column layout="total, sizes, prev, pager, next, jumper" :total="performanceRecordsTable.total"
prop="resource_id" @size-change="loadPerformanceRecordsList()" @current-change="loadPerformanceRecordsList" />
:label="t('resourceId')" </div>
min-width="120" </div>
:show-overflow-tooltip="true"
/> <edit ref="editPerformanceRecordsDialog" @complete="loadPerformanceRecordsList" />
</el-card>
<el-table-column </div>
prop="order_id"
:label="t('orderId')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="order_status"
:label="t('orderStatus')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="performance_type"
:label="t('performanceType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="performance_value"
:label="t('performanceValue')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="remarks"
:label="t('remarks')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="performanceRecordsTable.page"
v-model:page-size="performanceRecordsTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="performanceRecordsTable.total"
@size-change="loadPerformanceRecordsList()"
@current-change="loadPerformanceRecordsList"
/>
</div>
</div>
<edit
ref="editPerformanceRecordsDialog"
@complete="loadPerformanceRecordsList"
/>
</el-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
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 { import { getPerformanceRecordsList, deletePerformanceRecords, getWithPersonnelList, getWithCustomerResourcesList, getWithOrderTableList } from '@/app/api/performance_records'
getPerformanceRecordsList,
deletePerformanceRecords,
} from '@/app/api/performance_records'
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/performance_records/components/performance-records-edit.vue' import Edit from '@/app/views/performance_records/components/performance-records-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;
let performanceRecordsTable = reactive({ let performanceRecordsTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam:{
staff_id: '', "staff_id":"",
resource_id: '', "order_status":"",
order_id: '', "created_at":[]
order_status: '', }
performance_type: '',
performance_value: '',
remarks: '',
},
}) })
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
@ -204,26 +130,34 @@ const searchFormRef = ref<FormInstance>()
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
// //
const order_statusList = ref([] as any[])
const order_statusDictList = async () => {
order_statusList.value = await (await useDictionary('orderstatus')).data.dictionary
}
order_statusDictList();
const performance_typeList = ref([] as any[])
const performance_typeDictList = async () => {
performance_typeList.value = await (await useDictionary('performance_type')).data.dictionary
}
performance_typeDictList();
/** /**
* 获取绩效记录列表 * 获取绩效记录列表
*/ */
const loadPerformanceRecordsList = (page: number = 1) => { const loadPerformanceRecordsList = (page: number = 1) => {
performanceRecordsTable.loading = true performanceRecordsTable.loading = true
performanceRecordsTable.page = page performanceRecordsTable.page = page
getPerformanceRecordsList({ getPerformanceRecordsList({
page: performanceRecordsTable.page, page: performanceRecordsTable.page,
limit: performanceRecordsTable.limit, limit: performanceRecordsTable.limit,
...performanceRecordsTable.searchParam, ...performanceRecordsTable.searchParam
}) }).then(res => {
.then((res) => { performanceRecordsTable.loading = false
performanceRecordsTable.loading = false performanceRecordsTable.data = res.data.data
performanceRecordsTable.data = res.data.data performanceRecordsTable.total = res.data.total
performanceRecordsTable.total = res.data.total }).catch(() => {
}) performanceRecordsTable.loading = false
.catch(() => {
performanceRecordsTable.loading = false
}) })
} }
loadPerformanceRecordsList() loadPerformanceRecordsList()
@ -234,8 +168,8 @@ const editPerformanceRecordsDialog: Record<string, any> | null = ref(null)
* 添加绩效记录 * 添加绩效记录
*/ */
const addEvent = () => { const addEvent = () => {
editPerformanceRecordsDialog.value.setFormData() editPerformanceRecordsDialog.value.setFormData()
editPerformanceRecordsDialog.value.showDialog = true editPerformanceRecordsDialog.value.showDialog = true
} }
/** /**
@ -243,42 +177,60 @@ const addEvent = () => {
* @param data * @param data
*/ */
const editEvent = (data: any) => { const editEvent = (data: any) => {
editPerformanceRecordsDialog.value.setFormData(data) editPerformanceRecordsDialog.value.setFormData(data)
editPerformanceRecordsDialog.value.showDialog = true editPerformanceRecordsDialog.value.showDialog = true
} }
/** /**
* 删除绩效记录 * 删除绩效记录
*/ */
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('performanceRecordsDeleteTips'), t('warning'), { ElMessageBox.confirm(t('performanceRecordsDeleteTips'), t('warning'),
confirmButtonText: t('confirm'), {
cancelButtonText: t('cancel'), confirmButtonText: t('confirm'),
type: 'warning', cancelButtonText: t('cancel'),
}).then(() => { type: 'warning',
deletePerformanceRecords(id) }
.then(() => { ).then(() => {
loadPerformanceRecordsList() deletePerformanceRecords(id).then(() => {
}) loadPerformanceRecordsList()
.catch(() => {}) }).catch(() => {
}) })
})
} }
const staffIdList = ref([])
const setStaffIdList = async () => {
staffIdList.value = await (await getWithPersonnelList({})).data
}
setStaffIdList()
const resourceIdList = ref([])
const setResourceIdList = async () => {
resourceIdList.value = await (await getWithCustomerResourcesList({})).data
}
setResourceIdList()
const orderIdList = ref([])
const setOrderIdList = async () => {
orderIdList.value = await (await getWithOrderTableList({})).data
}
setOrderIdList()
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
formEl.resetFields() formEl.resetFields()
loadPerformanceRecordsList() loadPerformanceRecordsList()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */ /* 多行超出隐藏 */
.multi-hidden { .multi-hidden {
word-break: break-all; word-break: break-all;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
</style> </style>

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

@ -0,0 +1,315 @@
<template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updateStudent') : t('addStudent')" 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="t('userId')" prop="user_id">
<el-select class="input-width" v-model="formData.user_id" clearable :placeholder="t('userIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in userIdList"
:key="index"
:label="item['nickname']"
:value="item['member_id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('name')" prop="name">
<el-input v-model="formData.name" clearable :placeholder="t('namePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('gender')" prop="gender">
<el-select class="input-width" v-model="formData.gender" clearable :placeholder="t('genderPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in genderList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('age')" >
<el-input v-model="formData.age" clearable :placeholder="t('agePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('birthday')" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.birthday"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('birthdayPlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('emergencyContact')" >
<el-input v-model="formData.emergency_contact" clearable :placeholder="t('emergencyContactPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('contactPhone')" >
<el-input v-model="formData.contact_phone" clearable :placeholder="t('contactPhonePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('note')" >
<el-input v-model="formData.note" clearable :placeholder="t('notePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="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 { addStudent, editStudent, getStudentInfo, getWithCampusList, getWithClassGradeList, getWithMemberList } from '@/app/api/student'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
campus_id: '',
class_id: '',
user_id: '',
name: '',
gender: '',
age: '',
birthday: '',
emergency_contact: '',
contact_phone: '',
note: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
campus_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
]
,
class_id: [
{ required: true, message: t('classIdPlaceholder'), trigger: 'blur' },
]
,
user_id: [
{ required: true, message: t('userIdPlaceholder'), trigger: 'blur' },
]
,
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
gender: [
{ required: true, message: t('genderPlaceholder'), trigger: 'blur' },
]
,
age: [
{ required: true, message: t('agePlaceholder'), trigger: 'blur' },
]
,
birthday: [
{ required: true, message: t('birthdayPlaceholder'), trigger: 'blur' },
]
,
emergency_contact: [
{ required: true, message: t('emergencyContactPlaceholder'), trigger: 'blur' },
]
,
contact_phone: [
{ required: true, message: t('contactPhonePlaceholder'), trigger: 'blur' },
]
,
note: [
{ required: true, message: t('notePlaceholder'), 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 ? editStudent : addStudent
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 genderList = ref([])
const genderDictList = async () => {
genderList.value = await (await useDictionary('gender')).data.dictionary
}
genderDictList();
watch(() => genderList.value, () => { formData.gender = genderList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('xy_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const campusIdList = ref([] as any[])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const classIdList = ref([] as any[])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassGradeList({})).data
}
setClassIdList()
const userIdList = ref([] as any[])
const setUserIdList = async () => {
userIdList.value = await (await getWithMemberList({})).data
}
setUserIdList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if(row){
const data = await (await getStudentInfo(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>

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

@ -0,0 +1,240 @@
<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('addStudent') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="studentTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('campusId')" prop="campus_id">
<el-select class="w-[280px]" v-model="studentTable.searchParam.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
<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('name')" prop="name">
<el-input v-model="studentTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('emergencyContact')" prop="emergency_contact">
<el-input v-model="studentTable.searchParam.emergency_contact" :placeholder="t('emergencyContactPlaceholder')" />
</el-form-item>
<el-form-item :label="t('contactPhone')" prop="contact_phone">
<el-input v-model="studentTable.searchParam.contact_phone" :placeholder="t('contactPhonePlaceholder')" />
</el-form-item>
<el-form-item :label="t('createdAt')" prop="created_at">
<el-date-picker v-model="studentTable.searchParam.created_at" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadStudentList()">{{ 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="studentTable.data" size="large" v-loading="studentTable.loading">
<template #empty>
<span>{{ !studentTable.loading ? t('emptyData') : '' }}</span>
</template>
<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="user_id_name" :label="t('userId')" 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">
<template #default="{ row }">
<div v-for="(item, index) in genderList">
<div v-if="item.value == row.gender">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column prop="age" :label="t('age')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="emergency_contact" :label="t('emergencyContact')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="contact_phone" :label="t('contactPhone')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="note" :label="t('note')" min-width="120" :show-overflow-tooltip="true"/>
<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="studentTable.page" v-model:page-size="studentTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="studentTable.total"
@size-change="loadStudentList()" @current-change="loadStudentList" />
</div>
</div>
<edit ref="editStudentDialog" @complete="loadStudentList" />
</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 { getStudentList, deleteStudent, getWithCampusList, getWithClassGradeList, getWithMemberList } from '@/app/api/student'
import { img } from '@/utils/common'
import { ElMessageBox,FormInstance } from 'element-plus'
import Edit from '@/app/views/student/components/student-edit.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title;
let studentTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"campus_id":"",
"name":"",
"emergency_contact":"",
"contact_phone":"",
"created_at":[]
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const genderList = ref([] as any[])
const genderDictList = async () => {
genderList.value = await (await useDictionary('gender')).data.dictionary
}
genderDictList();
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('xy_status')).data.dictionary
}
statusDictList();
/**
* 获取学员列表
*/
const loadStudentList = (page: number = 1) => {
studentTable.loading = true
studentTable.page = page
getStudentList({
page: studentTable.page,
limit: studentTable.limit,
...studentTable.searchParam
}).then(res => {
studentTable.loading = false
studentTable.data = res.data.data
studentTable.total = res.data.total
}).catch(() => {
studentTable.loading = false
})
}
loadStudentList()
const editStudentDialog: Record<string, any> | null = ref(null)
/**
* 添加学员
*/
const addEvent = () => {
editStudentDialog.value.setFormData()
editStudentDialog.value.showDialog = true
}
/**
* 编辑学员
* @param data
*/
const editEvent = (data: any) => {
editStudentDialog.value.setFormData(data)
editStudentDialog.value.showDialog = true
}
/**
* 删除学员
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('studentDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteStudent(id).then(() => {
loadStudentList()
}).catch(() => {
})
})
}
const campusIdList = ref([])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const classIdList = ref([])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassGradeList({})).data
}
setClassIdList()
const userIdList = ref([])
const setUserIdList = async () => {
userIdList.value = await (await getWithMemberList({})).data
}
setUserIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadStudentList()
}
</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>

32
niucloud/app/adminapi/controller/attendance/Attendance.php

@ -31,13 +31,7 @@ class Attendance extends BaseAdminController
["campus_id",""], ["campus_id",""],
["staff_id",""], ["staff_id",""],
["attendance_date",""], ["attendance_date",""],
["check_in_time",""], ["status",""]
["check_out_time",""],
["status",""],
["remarks",""],
["created_at",""],
["updated_at",""],
["coordinate",""]
]); ]);
return success((new AttendanceService())->getPage($data)); return success((new AttendanceService())->getPage($data));
} }
@ -59,14 +53,12 @@ class Attendance extends BaseAdminController
$data = $this->request->params([ $data = $this->request->params([
["campus_id",0], ["campus_id",0],
["staff_id",0], ["staff_id",0],
["attendance_date","2025-05-16 17:06:01"], ["attendance_date","2025-05-23 12:44:09"],
["check_in_time",""], ["check_in_time",""],
["check_out_time",""], ["check_out_time",""],
["status",""],
["remarks",""], ["remarks",""],
["created_at",1747386361], ["status",""],
["updated_at",1747386361],
["coordinate",""]
]); ]);
$this->validate($data, 'app\validate\attendance\Attendance.add'); $this->validate($data, 'app\validate\attendance\Attendance.add');
$id = (new AttendanceService())->add($data); $id = (new AttendanceService())->add($data);
@ -82,14 +74,12 @@ class Attendance extends BaseAdminController
$data = $this->request->params([ $data = $this->request->params([
["campus_id",0], ["campus_id",0],
["staff_id",0], ["staff_id",0],
["attendance_date","2025-05-16 17:06:01"], ["attendance_date","2025-05-23 12:44:09"],
["check_in_time",""], ["check_in_time",""],
["check_out_time",""], ["check_out_time",""],
["status",""],
["remarks",""], ["remarks",""],
["created_at",1747386361], ["status",""],
["updated_at",1747386361],
["coordinate",""]
]); ]);
$this->validate($data, 'app\validate\attendance\Attendance.edit'); $this->validate($data, 'app\validate\attendance\Attendance.edit');
(new AttendanceService())->edit($id, $data); (new AttendanceService())->edit($id, $data);
@ -107,4 +97,12 @@ class Attendance extends BaseAdminController
} }
public function getCampusAll(){
return success(( new AttendanceService())->getCampusAll());
}
public function getPersonnelAll(){
return success(( new AttendanceService())->getPersonnelAll());
}
} }

15
niucloud/app/adminapi/controller/contract/Contract.php

@ -28,14 +28,9 @@ class Contract extends BaseAdminController
*/ */
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["contract_name",""],
["contract_template",""],
["contract_status",""], ["contract_status",""],
["contract_type",""], ["contract_type",""],
["remarks",""], ["created_at",["",""]]
["created_at",""],
["updated_at",""],
["deleted_at",""]
]); ]);
return success((new ContractService())->getPage($data)); return success((new ContractService())->getPage($data));
} }
@ -60,9 +55,7 @@ class Contract extends BaseAdminController
["contract_status",""], ["contract_status",""],
["contract_type",""], ["contract_type",""],
["remarks",""], ["remarks",""],
["created_at",1747387361],
["updated_at",1747387361],
["deleted_at",1747387361]
]); ]);
$this->validate($data, 'app\validate\contract\Contract.add'); $this->validate($data, 'app\validate\contract\Contract.add');
$id = (new ContractService())->add($data); $id = (new ContractService())->add($data);
@ -81,9 +74,7 @@ class Contract extends BaseAdminController
["contract_status",""], ["contract_status",""],
["contract_type",""], ["contract_type",""],
["remarks",""], ["remarks",""],
["created_at",1747387361],
["updated_at",1747387361],
["deleted_at",1747387361]
]); ]);
$this->validate($data, 'app\validate\contract\Contract.edit'); $this->validate($data, 'app\validate\contract\Contract.edit');
(new ContractService())->edit($id, $data); (new ContractService())->edit($id, $data);

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

@ -81,7 +81,7 @@ class CustomerResources extends BaseAdminController
["communication",""], ["communication",""],
["promised_visit_time",""], ["promised_visit_time",""],
["actual_visit_time",""], ["actual_visit_time",""],
["call_intent",""], ["call_intent","low"],
["first_visit_status",""], ["first_visit_status",""],
["second_visit_status",""], ["second_visit_status",""],
["is_closed",""] ["is_closed",""]
@ -124,7 +124,7 @@ class CustomerResources extends BaseAdminController
["communication",""], ["communication",""],
["promised_visit_time",""], ["promised_visit_time",""],
["actual_visit_time",""], ["actual_visit_time",""],
["call_intent",""], ["call_intent","low"],
["first_visit_status",""], ["first_visit_status",""],
["second_visit_status",""], ["second_visit_status",""],
["is_closed",""] ["is_closed",""]

18
niucloud/app/adminapi/controller/exam_answers/ExamAnswers.php

@ -28,11 +28,7 @@ class ExamAnswers extends BaseAdminController
*/ */
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["campus_id",""], ["campus_id",""]
["user_id",""],
["question_id",""],
["answer",""],
["is_correct",""]
]); ]);
return success((new ExamAnswersService())->getPage($data)); return success((new ExamAnswersService())->getPage($data));
} }
@ -94,4 +90,16 @@ class ExamAnswers extends BaseAdminController
} }
public function getCampusAll(){
return success(( new ExamAnswersService())->getCampusAll());
}
public function getPersonnelAll(){
return success(( new ExamAnswersService())->getPersonnelAll());
}
public function getExamQuestionsAll(){
return success(( new ExamAnswersService())->getExamQuestionsAll());
}
} }

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

@ -29,8 +29,7 @@ class ExamPapers extends BaseAdminController
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["selection_mode",""], ["selection_mode",""],
["total_score",""], ["created_at",["",""]]
["passing_score",""]
]); ]);
return success((new ExamPapersService())->getPage($data)); return success((new ExamPapersService())->getPage($data));
} }
@ -50,7 +49,9 @@ class ExamPapers extends BaseAdminController
*/ */
public function add(){ public function add(){
$data = $this->request->params([ $data = $this->request->params([
["title",""],
["selection_mode",""], ["selection_mode",""],
["questions_ids",""],
["total_score",0.00], ["total_score",0.00],
["passing_score",0.00], ["passing_score",0.00],
@ -67,7 +68,9 @@ class ExamPapers extends BaseAdminController
*/ */
public function edit(int $id){ public function edit(int $id){
$data = $this->request->params([ $data = $this->request->params([
["title",""],
["selection_mode",""], ["selection_mode",""],
["questions_ids",""],
["total_score",0.00], ["total_score",0.00],
["passing_score",0.00], ["passing_score",0.00],

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

@ -28,18 +28,9 @@ class ExamQuestions extends BaseAdminController
*/ */
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["title",""],
["question_type",""], ["question_type",""],
["question_content_type",""], ["created_at",["",""]]
["question_content",""],
["option_a_content_type",""],
["option_a_content",""],
["option_b_content_type",""],
["option_b_content",""],
["option_c_content_type",""],
["option_c_content",""],
["option_d_content_type",""],
["option_d_content",""],
["correct_answer",""]
]); ]);
return success((new ExamQuestionsService())->getPage($data)); return success((new ExamQuestionsService())->getPage($data));
} }
@ -59,18 +50,11 @@ class ExamQuestions extends BaseAdminController
*/ */
public function add(){ public function add(){
$data = $this->request->params([ $data = $this->request->params([
["title",""],
["question_type",""], ["question_type",""],
["question_content_type",""], ["question_content_type",""],
["question_content",""], ["question_content",""],
["option_a_content_type",""], ["option_json",[]]
["option_a_content",""],
["option_b_content_type",""],
["option_b_content",""],
["option_c_content_type",""],
["option_c_content",""],
["option_d_content_type",""],
["option_d_content",""],
["correct_answer",""],
]); ]);
$this->validate($data, 'app\validate\exam_questions\ExamQuestions.add'); $this->validate($data, 'app\validate\exam_questions\ExamQuestions.add');
@ -85,18 +69,11 @@ class ExamQuestions extends BaseAdminController
*/ */
public function edit(int $id){ public function edit(int $id){
$data = $this->request->params([ $data = $this->request->params([
["title",""],
["question_type",""], ["question_type",""],
["question_content_type",""], ["question_content_type",""],
["question_content",""], ["question_content",""],
["option_a_content_type",""], ["option_json",[]]
["option_a_content",""],
["option_b_content_type",""],
["option_b_content",""],
["option_c_content_type",""],
["option_c_content",""],
["option_d_content_type",""],
["option_d_content",""],
["correct_answer",""],
]); ]);
$this->validate($data, 'app\validate\exam_questions\ExamQuestions.edit'); $this->validate($data, 'app\validate\exam_questions\ExamQuestions.edit');

26
niucloud/app/adminapi/controller/exam_records/ExamRecords.php

@ -29,12 +29,8 @@ class ExamRecords extends BaseAdminController
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["campus_id",""], ["campus_id",""],
["user_id",""],
["paper_id",""], ["paper_id",""],
["score",""], ["status",""]
["status",""],
["start_time",""],
["end_time",""]
]); ]);
return success((new ExamRecordsService())->getPage($data)); return success((new ExamRecordsService())->getPage($data));
} }
@ -59,8 +55,8 @@ class ExamRecords extends BaseAdminController
["paper_id",0], ["paper_id",0],
["score",0.00], ["score",0.00],
["status",""], ["status",""],
["start_time","2025-05-16 17:43:09"], ["start_time","2025-05-23 17:15:15"],
["end_time","2025-05-16 17:43:09"], ["end_time","2025-05-23 17:15:15"],
]); ]);
$this->validate($data, 'app\validate\exam_records\ExamRecords.add'); $this->validate($data, 'app\validate\exam_records\ExamRecords.add');
@ -80,8 +76,8 @@ class ExamRecords extends BaseAdminController
["paper_id",0], ["paper_id",0],
["score",0.00], ["score",0.00],
["status",""], ["status",""],
["start_time","2025-05-16 17:43:09"], ["start_time","2025-05-23 17:15:15"],
["end_time","2025-05-16 17:43:09"], ["end_time","2025-05-23 17:15:15"],
]); ]);
$this->validate($data, 'app\validate\exam_records\ExamRecords.edit'); $this->validate($data, 'app\validate\exam_records\ExamRecords.edit');
@ -100,4 +96,16 @@ class ExamRecords extends BaseAdminController
} }
public function getCampusAll(){
return success(( new ExamRecordsService())->getCampusAll());
}
public function getPersonnelAll(){
return success(( new ExamRecordsService())->getPersonnelAll());
}
public function getExamPapersAll(){
return success(( new ExamRecordsService())->getExamPapersAll());
}
} }

20
niucloud/app/adminapi/controller/performance_records/PerformanceRecords.php

@ -29,12 +29,8 @@ class PerformanceRecords extends BaseAdminController
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["staff_id",""], ["staff_id",""],
["resource_id",""],
["order_id",""],
["order_status",""], ["order_status",""],
["performance_type",""], ["created_at",["",""]]
["performance_value",""],
["remarks",""]
]); ]);
return success((new PerformanceRecordsService())->getPage($data)); return success((new PerformanceRecordsService())->getPage($data));
} }
@ -56,7 +52,6 @@ class PerformanceRecords extends BaseAdminController
$data = $this->request->params([ $data = $this->request->params([
["staff_id",0], ["staff_id",0],
["resource_id",0], ["resource_id",0],
["order_id",0],
["order_status",""], ["order_status",""],
["performance_type",""], ["performance_type",""],
["performance_value",0.00], ["performance_value",0.00],
@ -77,7 +72,6 @@ class PerformanceRecords extends BaseAdminController
$data = $this->request->params([ $data = $this->request->params([
["staff_id",0], ["staff_id",0],
["resource_id",0], ["resource_id",0],
["order_id",0],
["order_status",""], ["order_status",""],
["performance_type",""], ["performance_type",""],
["performance_value",0.00], ["performance_value",0.00],
@ -100,4 +94,16 @@ class PerformanceRecords extends BaseAdminController
} }
public function getPersonnelAll(){
return success(( new PerformanceRecordsService())->getPersonnelAll());
}
public function getCustomerResourcesAll(){
return success(( new PerformanceRecordsService())->getCustomerResourcesAll());
}
public function getOrderTableAll(){
return success(( new PerformanceRecordsService())->getOrderTableAll());
}
} }

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

@ -0,0 +1,121 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\adminapi\controller\student;
use core\base\BaseAdminController;
use app\service\admin\student\StudentService;
/**
* 学员控制器
* Class Student
* @package app\adminapi\controller\student
*/
class Student extends BaseAdminController
{
/**
* 获取学员列表
* @return \think\Response
*/
public function lists(){
$data = $this->request->params([
["campus_id",""],
["name",""],
["emergency_contact",""],
["contact_phone",""],
["created_at",["",""]]
]);
return success((new StudentService())->getPage($data));
}
/**
* 学员详情
* @param int $id
* @return \think\Response
*/
public function info(int $id){
return success((new StudentService())->getInfo($id));
}
/**
* 添加学员
* @return \think\Response
*/
public function add(){
$data = $this->request->params([
["campus_id",0],
["class_id",0],
["user_id",0],
["name",""],
["gender",0],
["age",0.00],
["birthday","2025-05-23 17:31:39"],
["emergency_contact",""],
["contact_phone",""],
["note",""],
["status",0],
]);
$this->validate($data, 'app\validate\student\Student.add');
$id = (new StudentService())->add($data);
return success('ADD_SUCCESS', ['id' => $id]);
}
/**
* 学员编辑
* @param $id 学员id
* @return \think\Response
*/
public function edit(int $id){
$data = $this->request->params([
["campus_id",0],
["class_id",0],
["user_id",0],
["name",""],
["gender",0],
["age",0.00],
["birthday","2025-05-23 17:31:39"],
["emergency_contact",""],
["contact_phone",""],
["note",""],
["status",0],
]);
$this->validate($data, 'app\validate\student\Student.edit');
(new StudentService())->edit($id, $data);
return success('EDIT_SUCCESS');
}
/**
* 学员删除
* @param $id 学员id
* @return \think\Response
*/
public function del(int $id){
(new StudentService())->del($id);
return success('DELETE_SUCCESS');
}
public function getCampusAll(){
return success(( new StudentService())->getCampusAll());
}
public function getClassGradeAll(){
return success(( new StudentService())->getClassGradeAll());
}
public function getMemberAll(){
return success(( new StudentService())->getMemberAll());
}
}

9
niucloud/app/adminapi/route/attendance.php

@ -16,6 +16,11 @@ use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- attendance // USER_CODE_BEGIN -- attendance
Route::group('attendance', function () { Route::group('attendance', function () {
@ -31,6 +36,10 @@ Route::group('attendance', function () {
//删除考勤 //删除考勤
Route::delete('attendance/:id', 'attendance.Attendance/del'); Route::delete('attendance/:id', 'attendance.Attendance/del');
Route::get('campus_all','attendance.Attendance/getCampusAll');
Route::get('personnel_all','attendance.Attendance/getPersonnelAll');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

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

@ -14,6 +14,8 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- contract // USER_CODE_BEGIN -- contract
Route::group('contract', function () { Route::group('contract', function () {

9
niucloud/app/adminapi/route/exam_answers.php

@ -14,6 +14,9 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- exam_answers // USER_CODE_BEGIN -- exam_answers
Route::group('exam_answers', function () { Route::group('exam_answers', function () {
@ -29,6 +32,12 @@ Route::group('exam_answers', function () {
//删除答题记录 //删除答题记录
Route::delete('exam_answers/:id', 'exam_answers.ExamAnswers/del'); Route::delete('exam_answers/:id', 'exam_answers.ExamAnswers/del');
Route::get('campus_all','exam_answers.ExamAnswers/getCampusAll');
Route::get('personnel_all','exam_answers.ExamAnswers/getPersonnelAll');
Route::get('exam_questions_all','exam_answers.ExamAnswers/getExamQuestionsAll');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

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

@ -14,6 +14,7 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- exam_papers // USER_CODE_BEGIN -- exam_papers
Route::group('exam_papers', function () { Route::group('exam_papers', function () {

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

@ -14,6 +14,7 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- exam_questions // USER_CODE_BEGIN -- exam_questions
Route::group('exam_questions', function () { Route::group('exam_questions', function () {

7
niucloud/app/adminapi/route/exam_records.php

@ -14,6 +14,7 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- exam_records // USER_CODE_BEGIN -- exam_records
Route::group('exam_records', function () { Route::group('exam_records', function () {
@ -29,6 +30,12 @@ Route::group('exam_records', function () {
//删除考试记录 //删除考试记录
Route::delete('exam_records/:id', 'exam_records.ExamRecords/del'); Route::delete('exam_records/:id', 'exam_records.ExamRecords/del');
Route::get('campus_all','exam_records.ExamRecords/getCampusAll');
Route::get('personnel_all','exam_records.ExamRecords/getPersonnelAll');
Route::get('exam_papers_all','exam_records.ExamRecords/getExamPapersAll');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

7
niucloud/app/adminapi/route/performance_records.php

@ -14,6 +14,7 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- performance_records // USER_CODE_BEGIN -- performance_records
Route::group('performance_records', function () { Route::group('performance_records', function () {
@ -29,6 +30,12 @@ Route::group('performance_records', function () {
//删除绩效记录 //删除绩效记录
Route::delete('performance_records/:id', 'performance_records.PerformanceRecords/del'); Route::delete('performance_records/:id', 'performance_records.PerformanceRecords/del');
Route::get('personnel_all','performance_records.PerformanceRecords/getPersonnelAll');
Route::get('customer_resources_all','performance_records.PerformanceRecords/getCustomerResourcesAll');
Route::get('order_table_all','performance_records.PerformanceRecords/getOrderTableAll');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

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

@ -0,0 +1,44 @@
<?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 -- student
Route::group('student', function () {
//学员列表
Route::get('student', 'student.Student/lists');
//学员详情
Route::get('student/:id', 'student.Student/info');
//添加学员
Route::post('student', 'student.Student/add');
//编辑学员
Route::put('student/:id', 'student.Student/edit');
//删除学员
Route::delete('student/:id', 'student.Student/del');
Route::get('campus_all','student.Student/getCampusAll');
Route::get('class_grade_all','student.Student/getClassGradeAll');
Route::get('member_all','student.Student/getMemberAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- student

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

@ -9,22 +9,26 @@
// | Author: Niucloud Team // | Author: Niucloud Team
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
namespace app\model\campus; namespace app\model\attendance;
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;
use think\model\relation\HasOne; use think\model\relation\HasOne;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
/** /**
* 校区模型 * 考勤模型
* Class Campus * Class Attendance
* @package app\model\campus * @package app\model\attendance
*/ */
class Campus extends BaseModel class Attendance extends BaseModel
{ {
use SoftDelete;
/** /**
* 数据表主键 * 数据表主键
@ -36,53 +40,57 @@ class Campus extends BaseModel
* 模型名称 * 模型名称
* @var string * @var string
*/ */
protected $name = 'campus'; protected $name = 'attendance';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'delete_time';
/** /**
* 定义软删除字段的默认值. * 搜索器:考勤校区
* @var int * @param $value
*/ * @param $data
protected $defaultSoftDelete = 0; */
public function searchCampusIdAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_id", $value);
}
}
/** /**
* 搜索器:校区校区名称 * 搜索器:考勤人员
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusNameAttr($query, $value, $data) public function searchStaffIdAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("campus_name", "like", "%".$value."%"); $query->where("staff_id", $value);
} }
} }
/** /**
* 搜索器:校区校区地址 * 搜索器:考勤考勤日期
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusAddressAttr($query, $value, $data) public function searchAttendanceDateAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("campus_address", $value); $query->where("attendance_date", $value);
} }
} }
/** /**
* 搜索器:校区校区状态 * 搜索器:考勤考勤状态
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusStatusAttr($query, $value, $data) public function searchStatusAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("campus_status", $value); $query->where("status", $value);
} }
} }
@ -91,4 +99,12 @@ class Campus extends BaseModel
public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']);
}
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'staff_id')->joinType('left')->withField('name,id')->bind(['staff_id_name'=>'name']);
}
} }

132
niucloud/app/model/contract/Contract.php

@ -9,7 +9,7 @@
// | Author: Niucloud Team // | Author: Niucloud Team
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
namespace app\model\campus; namespace app\model\contract;
use core\base\BaseModel; use core\base\BaseModel;
use think\model\concern\SoftDelete; use think\model\concern\SoftDelete;
@ -17,14 +17,14 @@ use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
/** /**
* 校区模型 * 合同模型
* Class Campus * Class Contract
* @package app\model\campus * @package app\model\contract
*/ */
class Campus extends BaseModel class Contract extends BaseModel
{ {
use SoftDelete;
/** /**
* 数据表主键 * 数据表主键
@ -36,129 +36,59 @@ class Campus extends BaseModel
* 模型名称 * 模型名称
* @var string * @var string
*/ */
protected $name = 'campus'; protected $name = 'contract';
/** /**
* 搜索器:校区主键ID * 定义软删除标记字段.
* @param $value * @var string
* @param $data */
*/ protected $deleteTime = 'deleted_at';
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/** /**
* 搜索器:校区校区名称 * 定义软删除字段的默认值.
* @param $value * @var int
* @param $data */
*/ protected $defaultSoftDelete = 0;
public function searchCampusNameAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_name", $value);
}
}
/**
* 搜索器:校区校区地址
* @param $value
* @param $data
*/
public function searchCampusAddressAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_address", $value);
}
}
/** /**
* 搜索器:校区校区预览图,存储图片路径 * 搜索器:合同合同状态
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusPreviewImageAttr($query, $value, $data) public function searchContractStatusAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("campus_preview_image", $value); $query->where("contract_status", $value);
} }
} }
/** /**
* 搜索器:校区校区坐标,格式为经度,纬度 * 搜索器:合同合同类型
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusCoordinatesAttr($query, $value, $data) public function searchContractTypeAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("campus_coordinates", $value); $query->where("contract_type", $value);
} }
} }
/** /**
* 搜索器:校区校区介绍 * 搜索器:合同创建时间
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusIntroductionAttr($query, $value, $data) public function searchCreatedAtAttr($query, $value, $data)
{ {
if ($value) { $start = empty($value[0]) ? 0 : strtotime($value[0]);
$query->where("campus_introduction", $value); $end = empty($value[1]) ? 0 : strtotime($value[1]);
} if ($start > 0 && $end > 0) {
} $query->where([["created_at", "between", [$start, $end]]]);
} else if ($start > 0 && $end == 0) {
/** $query->where([["created_at", ">=", $start]]);
* 搜索器:校区校区状态:0-禁用,1-启用 } else if ($start == 0 && $end > 0) {
* @param $value $query->where([["created_at", "<=", $end]]);
* @param $data
*/
public function searchCampusStatusAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_status", $value);
}
}
/**
* 搜索器:校区校区创建时间
* @param $value
* @param $data
*/
public function searchCreateTimeAttr($query, $value, $data)
{
if ($value) {
$query->where("create_time", $value);
}
}
/**
* 搜索器:校区校区更新时间
* @param $value
* @param $data
*/
public function searchUpdateTimeAttr($query, $value, $data)
{
if ($value) {
$query->where("update_time", $value);
}
}
/**
* 搜索器:校区逻辑删除字段,NULL表示未删除,非空表示已删除
* @param $value
* @param $data
*/
public function searchDeleteTimeAttr($query, $value, $data)
{
if ($value) {
$query->where("delete_time", $value);
} }
} }

74
niucloud/app/model/exam_answers/ExamAnswers.php

@ -16,6 +16,12 @@ use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
use app\model\exam_questions\ExamQuestions;
/** /**
* 答题记录模型 * 答题记录模型
* Class ExamAnswers * Class ExamAnswers
@ -43,19 +49,7 @@ class ExamAnswers extends BaseModel
/** /**
* 搜索器:答题记录答题记录编号 * 搜索器:答题记录校区
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:答题记录校区ID
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -66,57 +60,21 @@ class ExamAnswers extends BaseModel
} }
} }
/**
* 搜索器:答题记录人员ID
* @param $value
* @param $data
*/
public function searchUserIdAttr($query, $value, $data)
{
if ($value) {
$query->where("user_id", $value);
}
}
/**
* 搜索器:答题记录试题ID
* @param $value
* @param $data
*/
public function searchQuestionIdAttr($query, $value, $data)
{
if ($value) {
$query->where("question_id", $value);
}
}
/**
* 搜索器:答题记录用户答案
* @param $value
* @param $data
*/
public function searchAnswerAttr($query, $value, $data)
{
if ($value) {
$query->where("answer", $value);
}
}
/**
* 搜索器:答题记录是否正确
* @param $value
* @param $data
*/
public function searchIsCorrectAttr($query, $value, $data)
{
if ($value) {
$query->where("is_correct", $value);
}
}
public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']);
}
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'user_id')->joinType('left')->withField('name,id')->bind(['user_id_name'=>'name']);
}
public function examQuestions(){
return $this->hasOne(ExamQuestions::class, 'id', 'question_id')->joinType('left')->withField('title,id')->bind(['question_id_name'=>'title']);
}
} }

40
niucloud/app/model/exam_papers/ExamPapers.php

@ -43,19 +43,7 @@ class ExamPapers extends BaseModel
/** /**
* 搜索器:试卷试卷编号 * 搜索器:试卷题目选择模式
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:试卷题目选择模式: random-随机主题, manual-自选题目
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -67,26 +55,20 @@ class ExamPapers extends BaseModel
} }
/** /**
* 搜索器:试卷总分 * 搜索器:试卷创建时间
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchTotalScoreAttr($query, $value, $data) public function searchCreatedAtAttr($query, $value, $data)
{ {
if ($value) { $start = empty($value[0]) ? 0 : strtotime($value[0]);
$query->where("total_score", $value); $end = empty($value[1]) ? 0 : strtotime($value[1]);
} if ($start > 0 && $end > 0) {
} $query->where([["created_at", "between", [$start, $end]]]);
} else if ($start > 0 && $end == 0) {
/** $query->where([["created_at", ">=", $start]]);
* 搜索器:试卷合格分数 } else if ($start == 0 && $end > 0) {
* @param $value $query->where([["created_at", "<=", $end]]);
* @param $data
*/
public function searchPassingScoreAttr($query, $value, $data)
{
if ($value) {
$query->where("passing_score", $value);
} }
} }

147
niucloud/app/model/exam_questions/ExamQuestions.php

@ -40,22 +40,27 @@ class ExamQuestions extends BaseModel
// 设置json类型字段
protected $json = [ 'option_json' ];
// 设置JSON数据返回数组
protected $jsonAssoc = true;
/** /**
* 搜索器:试题试题编号 * 搜索器:试题题目标题
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchIdAttr($query, $value, $data) public function searchTitleAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("id", $value); $query->where("title", $value);
} }
} }
/** /**
* 搜索器:试题题型: single_choice-单选, multiple_choice-多选, true_false-判断 * 搜索器:试题题型
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -67,134 +72,20 @@ class ExamQuestions extends BaseModel
} }
/** /**
* 搜索器:试题题干类型: text-文本, image-图片 * 搜索器:试题创建时间
* @param $value
* @param $data
*/
public function searchQuestionContentTypeAttr($query, $value, $data)
{
if ($value) {
$query->where("question_content_type", $value);
}
}
/**
* 搜索器:试题题干内容(如果是图片则存储URL)
* @param $value
* @param $data
*/
public function searchQuestionContentAttr($query, $value, $data)
{
if ($value) {
$query->where("question_content", $value);
}
}
/**
* 搜索器:试题选项A类型: text-文本, image-图片
* @param $value
* @param $data
*/
public function searchOptionAContentTypeAttr($query, $value, $data)
{
if ($value) {
$query->where("option_a_content_type", $value);
}
}
/**
* 搜索器:试题选项A内容(如果是图片则存储URL)
* @param $value
* @param $data
*/
public function searchOptionAContentAttr($query, $value, $data)
{
if ($value) {
$query->where("option_a_content", $value);
}
}
/**
* 搜索器:试题选项B类型: text-文本, image-图片
* @param $value
* @param $data
*/
public function searchOptionBContentTypeAttr($query, $value, $data)
{
if ($value) {
$query->where("option_b_content_type", $value);
}
}
/**
* 搜索器:试题选项B内容(如果是图片则存储URL)
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchOptionBContentAttr($query, $value, $data) public function searchCreatedAtAttr($query, $value, $data)
{ {
if ($value) { $start = empty($value[0]) ? 0 : strtotime($value[0]);
$query->where("option_b_content", $value); $end = empty($value[1]) ? 0 : strtotime($value[1]);
} if ($start > 0 && $end > 0) {
} $query->where([["created_at", "between", [$start, $end]]]);
} else if ($start > 0 && $end == 0) {
/** $query->where([["created_at", ">=", $start]]);
* 搜索器:试题选项C类型: text-文本, image-图片 } else if ($start == 0 && $end > 0) {
* @param $value $query->where([["created_at", "<=", $end]]);
* @param $data
*/
public function searchOptionCContentTypeAttr($query, $value, $data)
{
if ($value) {
$query->where("option_c_content_type", $value);
}
}
/**
* 搜索器:试题选项C内容(如果是图片则存储URL)
* @param $value
* @param $data
*/
public function searchOptionCContentAttr($query, $value, $data)
{
if ($value) {
$query->where("option_c_content", $value);
}
}
/**
* 搜索器:试题选项D类型: text-文本, image-图片
* @param $value
* @param $data
*/
public function searchOptionDContentTypeAttr($query, $value, $data)
{
if ($value) {
$query->where("option_d_content_type", $value);
}
}
/**
* 搜索器:试题选项D内容(如果是图片则存储URL)
* @param $value
* @param $data
*/
public function searchOptionDContentAttr($query, $value, $data)
{
if ($value) {
$query->where("option_d_content", $value);
}
}
/**
* 搜索器:试题正确答案(如果是多选,答案格式为如"A,B,D")
* @param $value
* @param $data
*/
public function searchCorrectAnswerAttr($query, $value, $data)
{
if ($value) {
$query->where("correct_answer", $value);
} }
} }

80
niucloud/app/model/exam_records/ExamRecords.php

@ -16,6 +16,12 @@ use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
use app\model\exam_papers\ExamPapers;
/** /**
* 考试记录模型 * 考试记录模型
* Class ExamRecords * Class ExamRecords
@ -43,19 +49,7 @@ class ExamRecords extends BaseModel
/** /**
* 搜索器:考试记录记录编号 * 搜索器:考试记录校区
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:考试记录校区ID
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -67,19 +61,7 @@ class ExamRecords extends BaseModel
} }
/** /**
* 搜索器:考试记录人员ID * 搜索器:考试记录试卷
* @param $value
* @param $data
*/
public function searchUserIdAttr($query, $value, $data)
{
if ($value) {
$query->where("user_id", $value);
}
}
/**
* 搜索器:考试记录试卷ID
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -91,19 +73,7 @@ class ExamRecords extends BaseModel
} }
/** /**
* 搜索器:考试记录得分 * 搜索器:考试记录考试状态
* @param $value
* @param $data
*/
public function searchScoreAttr($query, $value, $data)
{
if ($value) {
$query->where("score", $value);
}
}
/**
* 搜索器:考试记录考试状态: in_progress-进行中, completed-已完成
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -114,33 +84,21 @@ class ExamRecords extends BaseModel
} }
} }
/**
* 搜索器:考试记录考试开始时间
* @param $value
* @param $data
*/
public function searchStartTimeAttr($query, $value, $data)
{
if ($value) {
$query->where("start_time", $value);
}
}
/**
* 搜索器:考试记录考试结束时间
* @param $value
* @param $data
*/
public function searchEndTimeAttr($query, $value, $data)
{
if ($value) {
$query->where("end_time", $value);
}
}
public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']);
}
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'user_id')->joinType('left')->withField('name,id')->bind(['user_id_name'=>'name']);
}
public function examPapers(){
return $this->hasOne(ExamPapers::class, 'id', 'paper_id')->joinType('left')->withField('title,id')->bind(['paper_id_name'=>'title']);
}
} }

92
niucloud/app/model/performance_records/PerformanceRecords.php

@ -16,6 +16,12 @@ use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
use app\model\personnel\Personnel;
use app\model\customer_resources\CustomerResources;
use app\model\order_table\OrderTable;
/** /**
* 绩效记录模型 * 绩效记录模型
* Class PerformanceRecords * Class PerformanceRecords
@ -43,19 +49,7 @@ class PerformanceRecords extends BaseModel
/** /**
* 搜索器:绩效记录绩效编号 * 搜索器:绩效记录员工
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:绩效记录员工ID
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -67,31 +61,7 @@ class PerformanceRecords extends BaseModel
} }
/** /**
* 搜索器:绩效记录资源ID * 搜索器:绩效记录订单状态
* @param $value
* @param $data
*/
public function searchResourceIdAttr($query, $value, $data)
{
if ($value) {
$query->where("resource_id", $value);
}
}
/**
* 搜索器:绩效记录订单ID
* @param $value
* @param $data
*/
public function searchOrderIdAttr($query, $value, $data)
{
if ($value) {
$query->where("order_id", $value);
}
}
/**
* 搜索器:绩效记录订单状态: pending-待处理, completed-已完成, cancelled-已取消
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -103,44 +73,38 @@ class PerformanceRecords extends BaseModel
} }
/** /**
* 搜索器:绩效记录绩效类型: sales-销售绩效, marketing-市场绩效, other-其他 * 搜索器:绩效记录创建时间
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchPerformanceTypeAttr($query, $value, $data) public function searchCreatedAtAttr($query, $value, $data)
{ {
if ($value) { $start = empty($value[0]) ? 0 : strtotime($value[0]);
$query->where("performance_type", $value); $end = empty($value[1]) ? 0 : strtotime($value[1]);
if ($start > 0 && $end > 0) {
$query->where([["created_at", "between", [$start, $end]]]);
} else if ($start > 0 && $end == 0) {
$query->where([["created_at", ">=", $start]]);
} else if ($start == 0 && $end > 0) {
$query->where([["created_at", "<=", $end]]);
} }
} }
/**
* 搜索器:绩效记录绩效金额或分值
* @param $value
* @param $data
*/
public function searchPerformanceValueAttr($query, $value, $data)
{
if ($value) {
$query->where("performance_value", $value);
}
}
/**
* 搜索器:绩效记录备注
* @param $value
* @param $data
*/
public function searchRemarksAttr($query, $value, $data)
{
if ($value) {
$query->where("remarks", $value);
}
}
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'staff_id')->joinType('left')->withField('name,id')->bind(['staff_id_name'=>'name']);
}
public function customerResources(){
return $this->hasOne(CustomerResources::class, 'id', 'resource_id')->joinType('left')->withField('name,id')->bind(['resource_id_name'=>'name']);
}
public function orderTable(){
return $this->hasOne(OrderTable::class, 'id', 'order_id')->joinType('left')->withField('payment_id,id')->bind(['order_id_name'=>'payment_id']);
}
} }

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

@ -16,11 +16,21 @@ use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
use app\model\campus\Campus;
use app\model\class_grade\ClassGrade;
use app\model\member\Member;
/**
* 学员模型
* Class Student
* @package app\model\student
*/
class Student extends BaseModel class Student extends BaseModel
{ {
use SoftDelete;
use SoftDelete;
/** /**
* 数据表主键 * 数据表主键
@ -34,22 +44,99 @@ class Student extends BaseModel
*/ */
protected $name = 'student'; protected $name = 'student';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'deleted_at';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/** /**
* 定义软删除标记字段 * 搜索器:学员校区
* @var string * @param $value
* @param $data
*/ */
protected $deleteTime = 'deleted_at'; public function searchCampusIdAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_id", $value);
}
}
/** /**
* 定义软删除字段的默认值 * 搜索器:学员学员姓名
* @var int * @param $value
* @param $data
*/ */
protected $defaultSoftDelete = 0; public function searchNameAttr($query, $value, $data)
{
if ($value) {
$query->where("name", $value);
}
}
/**
* 搜索器:学员紧急联系人
* @param $value
* @param $data
*/
public function searchEmergencyContactAttr($query, $value, $data)
{
if ($value) {
$query->where("emergency_contact", $value);
}
}
/**
* 搜索器:学员联系人电话
* @param $value
* @param $data
*/
public function searchContactPhoneAttr($query, $value, $data)
{
if ($value) {
$query->where("contact_phone", $value);
}
}
/**
* 搜索器:学员创建时间
* @param $value
* @param $data
*/
public function searchCreatedAtAttr($query, $value, $data)
{
$start = empty($value[0]) ? 0 : strtotime($value[0]);
$end = empty($value[1]) ? 0 : strtotime($value[1]);
if ($start > 0 && $end > 0) {
$query->where([["created_at", "between", [$start, $end]]]);
} else if ($start > 0 && $end == 0) {
$query->where([["created_at", ">=", $start]]);
} else if ($start == 0 && $end > 0) {
$query->where([["created_at", "<=", $end]]);
}
}
public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']);
}
public function classGrade(){
return $this->hasOne(ClassGrade::class, 'id', 'class_id')->joinType('left')->withField('class_name,id')->bind(['class_id_name'=>'class_name']);
}
public function member(){
return $this->hasOne(Member::class, 'member_id', 'user_id')->joinType('left')->withField('nickname,member_id')->bind(['user_id_name'=>'nickname']);
}
} }

20
niucloud/app/service/admin/attendance/AttendanceService.php

@ -12,6 +12,8 @@
namespace app\service\admin\attendance; namespace app\service\admin\attendance;
use app\model\attendance\Attendance; use app\model\attendance\Attendance;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -36,10 +38,10 @@ class AttendanceService extends BaseAdminService
*/ */
public function getPage(array $where = []) public function getPage(array $where = [])
{ {
$field = 'id,campus_id,staff_id,attendance_date,check_in_time,check_out_time,status,remarks,created_at,updated_at,coordinate'; $field = 'id,campus_id,staff_id,attendance_date,check_in_time,check_out_time,remarks,status,created_at,updated_at,coordinate';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","campus_id","staff_id","attendance_date","check_in_time","check_out_time","status","remarks","created_at","updated_at","coordinate"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["campus_id","staff_id","attendance_date","status"], $where)->with(['campus','personnel'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -51,9 +53,9 @@ class AttendanceService extends BaseAdminService
*/ */
public function getInfo(int $id) public function getInfo(int $id)
{ {
$field = 'id,campus_id,staff_id,attendance_date,check_in_time,check_out_time,status,remarks,created_at,updated_at,coordinate'; $field = 'id,campus_id,staff_id,attendance_date,check_in_time,check_out_time,remarks,status,created_at,updated_at,coordinate';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['campus','personnel'])->findOrEmpty()->toArray();
return $info; return $info;
} }
@ -95,5 +97,15 @@ class AttendanceService extends BaseAdminService
} }
public function getCampusAll(){
$campusModel = new Campus();
return $campusModel->select()->toArray();
}
public function getPersonnelAll(){
$personnelModel = new Personnel();
return $personnelModel->select()->toArray();
}
} }

2
niucloud/app/service/admin/contract/ContractService.php

@ -39,7 +39,7 @@ class ContractService extends BaseAdminService
$field = 'id,contract_name,contract_template,contract_status,contract_type,remarks,created_at,updated_at,deleted_at'; $field = 'id,contract_name,contract_template,contract_status,contract_type,remarks,created_at,updated_at,deleted_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","contract_name","contract_template","contract_status","contract_type","remarks","created_at","updated_at","deleted_at"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["contract_status","contract_type","created_at"], $where)->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }

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

@ -191,7 +191,7 @@ class CustomerResourcesService extends BaseAdminService
// return fail("操作失败"); // return fail("操作失败");
// } // }
$data['consultant'] = 1; // $data['consultant'] = 1;
$res = $this->model->where([['id', '=', $id]])->findOrEmpty()->toArray(); $res = $this->model->where([['id', '=', $id]])->findOrEmpty()->toArray();
@ -222,7 +222,7 @@ class CustomerResourcesService extends BaseAdminService
if($resources_save['is_save']){ if($resources_save['is_save']){
$customerResourceChanges->insert([ $customerResourceChanges->insert([
'customer_resource_id' => $id, 'customer_resource_id' => $id,
'operator_id' => $data['consultant'], 'operator_id' => $res['consultant'],
'campus_id' => $data['campus'], 'campus_id' => $data['campus'],
'modified_fields' => $resources_save['modified_fields'], 'modified_fields' => $resources_save['modified_fields'],
'old_values' => $resources_save['old_values'], 'old_values' => $resources_save['old_values'],
@ -262,7 +262,7 @@ class CustomerResourcesService extends BaseAdminService
$sixSpeedModificationLog->insert([ $sixSpeedModificationLog->insert([
'customer_resource_id' => $id, 'customer_resource_id' => $id,
'operator_id' => $data['consultant'], 'operator_id' => $res['consultant'],
'campus_id' => $data['campus'], 'campus_id' => $data['campus'],
'modified_field' => $six_save['modified_fields'], 'modified_field' => $six_save['modified_fields'],
'old_value' => $six_save['old_values'], 'old_value' => $six_save['old_values'],

23
niucloud/app/service/admin/exam_answers/ExamAnswersService.php

@ -12,6 +12,9 @@
namespace app\service\admin\exam_answers; namespace app\service\admin\exam_answers;
use app\model\exam_answers\ExamAnswers; use app\model\exam_answers\ExamAnswers;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
use app\model\exam_questions\ExamQuestions;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -39,7 +42,7 @@ class ExamAnswersService extends BaseAdminService
$field = 'id,campus_id,user_id,question_id,answer,is_correct,created_at,updated_at'; $field = 'id,campus_id,user_id,question_id,answer,is_correct,created_at,updated_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","campus_id","user_id","question_id","answer","is_correct"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["campus_id"], $where)->with(['campus','personnel','examQuestions'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -53,7 +56,8 @@ class ExamAnswersService extends BaseAdminService
{ {
$field = 'id,campus_id,user_id,question_id,answer,is_correct,created_at,updated_at'; $field = 'id,campus_id,user_id,question_id,answer,is_correct,created_at,updated_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['campus','personnel','examQuestions'])->findOrEmpty()->toArray();
$info['is_correct'] = strval($info['is_correct']);
return $info; return $info;
} }
@ -95,5 +99,20 @@ class ExamAnswersService extends BaseAdminService
} }
public function getCampusAll(){
$campusModel = new Campus();
return $campusModel->select()->toArray();
}
public function getPersonnelAll(){
$personnelModel = new Personnel();
return $personnelModel->select()->toArray();
}
public function getExamQuestionsAll(){
$examQuestionsModel = new ExamQuestions();
return $examQuestionsModel->select()->toArray();
}
} }

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

@ -36,10 +36,10 @@ class ExamPapersService extends BaseAdminService
*/ */
public function getPage(array $where = []) public function getPage(array $where = [])
{ {
$field = 'id,selection_mode,total_score,passing_score,created_at,updated_at'; $field = 'id,title,selection_mode,questions_ids,total_score,passing_score,created_at,updated_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","selection_mode","total_score","passing_score"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["selection_mode","created_at"], $where)->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -51,7 +51,7 @@ class ExamPapersService extends BaseAdminService
*/ */
public function getInfo(int $id) public function getInfo(int $id)
{ {
$field = 'id,selection_mode,total_score,passing_score,created_at,updated_at'; $field = 'id,title,selection_mode,questions_ids,total_score,passing_score,created_at,updated_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;

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

@ -36,10 +36,10 @@ class ExamQuestionsService extends BaseAdminService
*/ */
public function getPage(array $where = []) public function getPage(array $where = [])
{ {
$field = 'id,question_type,question_content_type,question_content,option_a_content_type,option_a_content,option_b_content_type,option_b_content,option_c_content_type,option_c_content,option_d_content_type,option_d_content,correct_answer,created_at,updated_at'; $field = 'id,title,question_type,question_content_type,question_content,option_json,created_at,updated_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","question_type","question_content_type","question_content","option_a_content_type","option_a_content","option_b_content_type","option_b_content","option_c_content_type","option_c_content","option_d_content_type","option_d_content","correct_answer"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["title","question_type","created_at"], $where)->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -51,7 +51,7 @@ class ExamQuestionsService extends BaseAdminService
*/ */
public function getInfo(int $id) public function getInfo(int $id)
{ {
$field = 'id,question_type,question_content_type,question_content,option_a_content_type,option_a_content,option_b_content_type,option_b_content,option_c_content_type,option_c_content,option_d_content_type,option_d_content,correct_answer,created_at,updated_at'; $field = 'id,title,question_type,question_content_type,question_content,option_json,created_at,updated_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;

22
niucloud/app/service/admin/exam_records/ExamRecordsService.php

@ -12,6 +12,9 @@
namespace app\service\admin\exam_records; namespace app\service\admin\exam_records;
use app\model\exam_records\ExamRecords; use app\model\exam_records\ExamRecords;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
use app\model\exam_papers\ExamPapers;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -39,7 +42,7 @@ class ExamRecordsService extends BaseAdminService
$field = 'id,campus_id,user_id,paper_id,score,status,start_time,end_time,created_at,updated_at'; $field = 'id,campus_id,user_id,paper_id,score,status,start_time,end_time,created_at,updated_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","campus_id","user_id","paper_id","score","status","start_time","end_time"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["campus_id","paper_id","status"], $where)->with(['campus','personnel','examPapers'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -53,7 +56,7 @@ class ExamRecordsService extends BaseAdminService
{ {
$field = 'id,campus_id,user_id,paper_id,score,status,start_time,end_time,created_at,updated_at'; $field = 'id,campus_id,user_id,paper_id,score,status,start_time,end_time,created_at,updated_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['campus','personnel','examPapers'])->findOrEmpty()->toArray();
return $info; return $info;
} }
@ -95,5 +98,20 @@ class ExamRecordsService extends BaseAdminService
} }
public function getCampusAll(){
$campusModel = new Campus();
return $campusModel->select()->toArray();
}
public function getPersonnelAll(){
$personnelModel = new Personnel();
return $personnelModel->select()->toArray();
}
public function getExamPapersAll(){
$examPapersModel = new ExamPapers();
return $examPapersModel->select()->toArray();
}
} }

22
niucloud/app/service/admin/performance_records/PerformanceRecordsService.php

@ -12,6 +12,9 @@
namespace app\service\admin\performance_records; namespace app\service\admin\performance_records;
use app\model\performance_records\PerformanceRecords; use app\model\performance_records\PerformanceRecords;
use app\model\personnel\Personnel;
use app\model\customer_resources\CustomerResources;
use app\model\order_table\OrderTable;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -39,7 +42,7 @@ class PerformanceRecordsService extends BaseAdminService
$field = 'id,staff_id,resource_id,order_id,order_status,performance_type,performance_value,remarks,created_at,updated_at'; $field = 'id,staff_id,resource_id,order_id,order_status,performance_type,performance_value,remarks,created_at,updated_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","staff_id","resource_id","order_id","order_status","performance_type","performance_value","remarks"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["staff_id","order_status","created_at"], $where)->with(['personnel','customerResources','orderTable'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -53,7 +56,7 @@ class PerformanceRecordsService extends BaseAdminService
{ {
$field = 'id,staff_id,resource_id,order_id,order_status,performance_type,performance_value,remarks,created_at,updated_at'; $field = 'id,staff_id,resource_id,order_id,order_status,performance_type,performance_value,remarks,created_at,updated_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['personnel','customerResources','orderTable'])->findOrEmpty()->toArray();
return $info; return $info;
} }
@ -95,5 +98,20 @@ class PerformanceRecordsService extends BaseAdminService
} }
public function getPersonnelAll(){
$personnelModel = new Personnel();
return $personnelModel->select()->toArray();
}
public function getCustomerResourcesAll(){
$customerResourcesModel = new CustomerResources();
return $customerResourcesModel->select()->toArray();
}
public function getOrderTableAll(){
$orderTableModel = new OrderTable();
return $orderTableModel->select()->toArray();
}
} }

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

@ -0,0 +1,117 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\admin\student;
use app\model\student\Student;
use app\model\campus\Campus;
use app\model\class_grade\ClassGrade;
use app\model\member\Member;
use core\base\BaseAdminService;
/**
* 学员服务层
* Class StudentService
* @package app\service\admin\student
*/
class StudentService extends BaseAdminService
{
public function __construct()
{
parent::__construct();
$this->model = new Student();
}
/**
* 获取学员列表
* @param array $where
* @return array
*/
public function getPage(array $where = [])
{
$field = 'id,campus_id,class_id,user_id,name,gender,age,birthday,emergency_contact,contact_phone,note,status,created_at,updated_at,deleted_at';
$order = 'id asc';
$search_model = $this->model->withSearch(["campus_id","name","emergency_contact","contact_phone","created_at"], $where)->with(['campus','classGrade','member'])->field($field)->order($order);
$list = $this->pageQuery($search_model);
return $list;
}
/**
* 获取学员信息
* @param int $id
* @return array
*/
public function getInfo(int $id)
{
$field = 'id,campus_id,class_id,user_id,name,gender,age,birthday,emergency_contact,contact_phone,note,status,created_at,updated_at,deleted_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->with(['campus','classGrade','member'])->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 getCampusAll(){
$campusModel = new Campus();
return $campusModel->select()->toArray();
}
public function getClassGradeAll(){
$classGradeModel = new ClassGrade();
return $classGradeModel->select()->toArray();
}
public function getMemberAll(){
$memberModel = new Member();
return $memberModel->select()->toArray();
}
}

8
niucloud/app/validate/attendance/Attendance.php

@ -23,6 +23,8 @@ class Attendance extends BaseValidate
'campus_id' => 'require', 'campus_id' => 'require',
'staff_id' => 'require', 'staff_id' => 'require',
'attendance_date' => 'require', 'attendance_date' => 'require',
'check_in_time' => 'require',
'check_out_time' => 'require',
'status' => 'require', 'status' => 'require',
]; ];
@ -30,12 +32,14 @@ class Attendance extends BaseValidate
'campus_id.require' => ['common_validate.require', ['campus_id']], 'campus_id.require' => ['common_validate.require', ['campus_id']],
'staff_id.require' => ['common_validate.require', ['staff_id']], 'staff_id.require' => ['common_validate.require', ['staff_id']],
'attendance_date.require' => ['common_validate.require', ['attendance_date']], 'attendance_date.require' => ['common_validate.require', ['attendance_date']],
'check_in_time.require' => ['common_validate.require', ['check_in_time']],
'check_out_time.require' => ['common_validate.require', ['check_out_time']],
'status.require' => ['common_validate.require', ['status']], 'status.require' => ['common_validate.require', ['status']],
]; ];
protected $scene = [ protected $scene = [
"add" => ['campus_id', 'staff_id', 'attendance_date', 'check_in_time', 'check_out_time', 'status', 'remarks', 'created_at', 'updated_at', 'coordinate'], "add" => ['campus_id', 'staff_id', 'attendance_date', 'check_in_time', 'check_out_time', 'remarks', 'status'],
"edit" => ['campus_id', 'staff_id', 'attendance_date', 'check_in_time', 'check_out_time', 'status', 'remarks', 'created_at', 'updated_at', 'coordinate'] "edit" => ['campus_id', 'staff_id', 'attendance_date', 'check_in_time', 'check_out_time', 'remarks', 'status']
]; ];
} }

4
niucloud/app/validate/contract/Contract.php

@ -34,8 +34,8 @@ class Contract extends BaseValidate
]; ];
protected $scene = [ protected $scene = [
"add" => ['contract_name', 'contract_template', 'contract_status', 'contract_type', 'remarks', 'created_at', 'updated_at', 'deleted_at'], "add" => ['contract_name', 'contract_template', 'contract_status', 'contract_type', 'remarks'],
"edit" => ['contract_name', 'contract_template', 'contract_status', 'contract_type', 'remarks', 'created_at', 'updated_at', 'deleted_at'] "edit" => ['contract_name', 'contract_template', 'contract_status', 'contract_type', 'remarks']
]; ];
} }

10
niucloud/app/validate/exam_papers/ExamPapers.php

@ -21,19 +21,21 @@ class ExamPapers extends BaseValidate
protected $rule = [ protected $rule = [
'selection_mode' => 'require', 'selection_mode' => 'require',
'total_score' => 'require', 'total_score' => 'require|between:0,100',
'passing_score' => 'require', 'passing_score' => 'require|between:0,100',
]; ];
protected $message = [ protected $message = [
'selection_mode.require' => ['common_validate.require', ['selection_mode']], 'selection_mode.require' => ['common_validate.require', ['selection_mode']],
'total_score.require' => ['common_validate.require', ['total_score']], 'total_score.require' => ['common_validate.require', ['total_score']],
'total_score.between' => ['common_validate.between', ['total_score','0','100']],
'passing_score.require' => ['common_validate.require', ['passing_score']], 'passing_score.require' => ['common_validate.require', ['passing_score']],
'passing_score.between' => ['common_validate.between', ['passing_score','0','100']],
]; ];
protected $scene = [ protected $scene = [
"add" => ['selection_mode', 'total_score', 'passing_score'], "add" => ['selection_mode', 'questions_ids', 'total_score', 'passing_score'],
"edit" => ['selection_mode', 'total_score', 'passing_score'] "edit" => ['selection_mode', 'questions_ids', 'total_score', 'passing_score']
]; ];
} }

10
niucloud/app/validate/exam_questions/ExamQuestions.php

@ -20,22 +20,24 @@ class ExamQuestions extends BaseValidate
{ {
protected $rule = [ protected $rule = [
'title' => 'require',
'question_type' => 'require', 'question_type' => 'require',
'question_content_type' => 'require', 'question_content_type' => 'require',
'question_content' => 'require', 'question_content' => 'require',
'correct_answer' => 'require', 'option_json' => 'require',
]; ];
protected $message = [ protected $message = [
'title.require' => ['common_validate.require', ['title']],
'question_type.require' => ['common_validate.require', ['question_type']], 'question_type.require' => ['common_validate.require', ['question_type']],
'question_content_type.require' => ['common_validate.require', ['question_content_type']], 'question_content_type.require' => ['common_validate.require', ['question_content_type']],
'question_content.require' => ['common_validate.require', ['question_content']], 'question_content.require' => ['common_validate.require', ['question_content']],
'correct_answer.require' => ['common_validate.require', ['correct_answer']], 'option_json.require' => ['common_validate.require', ['option_json']],
]; ];
protected $scene = [ protected $scene = [
"add" => ['question_type', 'question_content_type', 'question_content', 'option_a_content_type', 'option_a_content', 'option_b_content_type', 'option_b_content', 'option_c_content_type', 'option_c_content', 'option_d_content_type', 'option_d_content', 'correct_answer'], "add" => ['title', 'question_type', 'question_content_type', 'question_content', 'option_json'],
"edit" => ['question_type', 'question_content_type', 'question_content', 'option_a_content_type', 'option_a_content', 'option_b_content_type', 'option_b_content', 'option_c_content_type', 'option_c_content', 'option_d_content_type', 'option_d_content', 'correct_answer'] "edit" => ['title', 'question_type', 'question_content_type', 'question_content', 'option_json']
]; ];
} }

2
niucloud/app/validate/exam_records/ExamRecords.php

@ -23,12 +23,14 @@ class ExamRecords extends BaseValidate
'campus_id' => 'require', 'campus_id' => 'require',
'user_id' => 'require', 'user_id' => 'require',
'paper_id' => 'require', 'paper_id' => 'require',
'score' => 'between:0,100',
]; ];
protected $message = [ protected $message = [
'campus_id.require' => ['common_validate.require', ['campus_id']], 'campus_id.require' => ['common_validate.require', ['campus_id']],
'user_id.require' => ['common_validate.require', ['user_id']], 'user_id.require' => ['common_validate.require', ['user_id']],
'paper_id.require' => ['common_validate.require', ['paper_id']], 'paper_id.require' => ['common_validate.require', ['paper_id']],
'score.between' => ['common_validate.between', ['score','0','100']],
]; ];
protected $scene = [ protected $scene = [

6
niucloud/app/validate/performance_records/PerformanceRecords.php

@ -22,6 +22,7 @@ class PerformanceRecords extends BaseValidate
protected $rule = [ protected $rule = [
'staff_id' => 'require', 'staff_id' => 'require',
'resource_id' => 'require', 'resource_id' => 'require',
'order_status' => 'require',
'performance_type' => 'require', 'performance_type' => 'require',
'performance_value' => 'require', 'performance_value' => 'require',
]; ];
@ -29,13 +30,14 @@ class PerformanceRecords extends BaseValidate
protected $message = [ protected $message = [
'staff_id.require' => ['common_validate.require', ['staff_id']], 'staff_id.require' => ['common_validate.require', ['staff_id']],
'resource_id.require' => ['common_validate.require', ['resource_id']], 'resource_id.require' => ['common_validate.require', ['resource_id']],
'order_status.require' => ['common_validate.require', ['order_status']],
'performance_type.require' => ['common_validate.require', ['performance_type']], 'performance_type.require' => ['common_validate.require', ['performance_type']],
'performance_value.require' => ['common_validate.require', ['performance_value']], 'performance_value.require' => ['common_validate.require', ['performance_value']],
]; ];
protected $scene = [ protected $scene = [
"add" => ['staff_id', 'resource_id', 'order_id', 'order_status', 'performance_type', 'performance_value', 'remarks'], "add" => ['staff_id', 'resource_id', 'order_status', 'performance_type', 'performance_value', 'remarks'],
"edit" => ['staff_id', 'resource_id', 'order_id', 'order_status', 'performance_type', 'performance_value', 'remarks'] "edit" => ['staff_id', 'resource_id', 'order_status', 'performance_type', 'performance_value', 'remarks']
]; ];
} }

43
niucloud/app/validate/student/Student.php

@ -0,0 +1,43 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\validate\student;
use core\base\BaseValidate;
/**
* 学员验证器
* Class Student
* @package addon\app\validate\student
*/
class Student extends BaseValidate
{
protected $rule = [
'class_id' => 'require',
'user_id' => 'require',
'name' => 'require',
'gender' => 'require',
'status' => 'require',
];
protected $message = [
'class_id.require' => ['common_validate.require', ['class_id']],
'user_id.require' => ['common_validate.require', ['user_id']],
'name.require' => ['common_validate.require', ['name']],
'gender.require' => ['common_validate.require', ['gender']],
'status.require' => ['common_validate.require', ['status']],
];
protected $scene = [
"add" => ['campus_id', 'class_id', 'user_id', 'name', 'gender', 'age', 'birthday', 'emergency_contact', 'contact_phone', 'note', 'status'],
"edit" => ['campus_id', 'class_id', 'user_id', 'name', 'gender', 'age', 'birthday', 'emergency_contact', 'contact_phone', 'note', 'status']
];
}
Loading…
Cancel
Save