Browse Source

Merge branch 'yuhongzhe' into wangzeyan

master
王泽彦 10 months ago
parent
commit
8d208a6015
  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. 48
      admin/src/app/lang/zh-cn/attendance.attendance.json
  10. 42
      admin/src/app/lang/zh-cn/contract.contract.json
  11. 35
      admin/src/app/lang/zh-cn/exam_answers.exam_answers.json
  12. 30
      admin/src/app/lang/zh-cn/exam_papers.exam_papers.json
  13. 53
      admin/src/app/lang/zh-cn/exam_questions.exam_questions.json
  14. 42
      admin/src/app/lang/zh-cn/exam_records.exam_records.json
  15. 42
      admin/src/app/lang/zh-cn/performance_records.performance_records.json
  16. 31
      admin/src/app/lang/zh-cn/student.student.json
  17. 559
      admin/src/app/views/attendance/attendance.vue
  18. 556
      admin/src/app/views/attendance/components/attendance-edit.vue
  19. 5
      admin/src/app/views/campus_person_role/campus_person_role.vue
  20. 484
      admin/src/app/views/contract/components/contract-edit.vue
  21. 506
      admin/src/app/views/contract/contract.vue
  22. 456
      admin/src/app/views/exam_answers/components/exam-answers-edit.vue
  23. 451
      admin/src/app/views/exam_answers/exam_answers.vue
  24. 487
      admin/src/app/views/exam_papers/components/exam-papers-edit.vue
  25. 415
      admin/src/app/views/exam_papers/exam_papers.vue
  26. 287
      admin/src/app/views/exam_questions/components/exam-questions-edit.vue
  27. 576
      admin/src/app/views/exam_questions/exam_questions.vue
  28. 516
      admin/src/app/views/exam_records/components/exam-records-edit.vue
  29. 508
      admin/src/app/views/exam_records/exam_records.vue
  30. 510
      admin/src/app/views/performance_records/components/performance-records-edit.vue
  31. 520
      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. 9
      niucloud/app/adminapi/controller/exam_papers/ExamPapers.php
  39. 37
      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. 68
      niucloud/app/model/attendance/Attendance.php
  52. 136
      niucloud/app/model/contract/Contract.php
  53. 78
      niucloud/app/model/exam_answers/ExamAnswers.php
  54. 40
      niucloud/app/model/exam_papers/ExamPapers.php
  55. 165
      niucloud/app/model/exam_questions/ExamQuestions.php
  56. 84
      niucloud/app/model/exam_records/ExamRecords.php
  57. 96
      niucloud/app/model/performance_records/PerformanceRecords.php
  58. 111
      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. 8
      niucloud/app/service/admin/exam_papers/ExamPapersService.php
  64. 8
      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

48
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": "请选择结束时间"
}

42
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": "请选择结束时间"
}

35
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": "请选择结束时间" }
}

30
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":"请选择结束时间"
}

53
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": "请选择结束时间"
}

42
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": "请选择结束时间"
}

42
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":"请选择结束时间"
}

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

@ -1,318 +1,241 @@
<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>
</template>
<el-table-column
prop="attendance_date" <script lang="ts" setup>
:label="t('attendanceDate')" import { reactive, ref, watch } from 'vue'
min-width="120" import { t } from '@/lang'
:show-overflow-tooltip="true" import { useDictionary } from '@/app/api/dict'
/> import { getAttendanceList, deleteAttendance, getWithCampusList, getWithPersonnelList } from '@/app/api/attendance'
import { img } from '@/utils/common'
<el-table-column import { ElMessageBox,FormInstance } from 'element-plus'
prop="check_in_time" import Edit from '@/app/views/attendance/components/attendance-edit.vue'
:label="t('checkInTime')" import { useRoute } from 'vue-router'
min-width="120" const route = useRoute()
:show-overflow-tooltip="true" const pageName = route.meta.title;
/>
let attendanceTable = reactive({
<el-table-column page: 1,
prop="check_out_time" limit: 10,
:label="t('checkOutTime')" total: 0,
min-width="120" loading: true,
:show-overflow-tooltip="true" data: [],
/> searchParam:{
"campus_id":"",
<el-table-column "staff_id":"",
prop="status" "attendance_date":"",
:label="t('status')" "status":""
min-width="120" }
:show-overflow-tooltip="true" })
/>
const searchFormRef = ref<FormInstance>()
<el-table-column
prop="remarks" //
:label="t('remarks')" const selectData = ref<any[]>([])
min-width="120"
:show-overflow-tooltip="true" //
/> const statusList = ref([] as any[])
const statusDictList = async () => {
<el-table-column statusList.value = await (await useDictionary('kq_status')).data.dictionary
prop="created_at" }
:label="t('createdAt')" statusDictList();
min-width="120"
:show-overflow-tooltip="true" /**
/> * 获取考勤列表
*/
<el-table-column const loadAttendanceList = (page: number = 1) => {
prop="updated_at" attendanceTable.loading = true
:label="t('updatedAt')" attendanceTable.page = page
min-width="120"
:show-overflow-tooltip="true" getAttendanceList({
/> page: attendanceTable.page,
limit: attendanceTable.limit,
<el-table-column ...attendanceTable.searchParam
prop="coordinate" }).then(res => {
:label="t('coordinate')" attendanceTable.loading = false
min-width="120" attendanceTable.data = res.data.data
:show-overflow-tooltip="true" attendanceTable.total = res.data.total
/> }).catch(() => {
attendanceTable.loading = false
<el-table-column })
:label="t('operation')" }
fixed="right" loadAttendanceList()
min-width="120"
> const editAttendanceDialog: Record<string, any> | null = ref(null)
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ /**
t('edit') * 添加考勤
}}</el-button> */
<el-button type="primary" link @click="deleteEvent(row.id)">{{ const addEvent = () => {
t('delete') editAttendanceDialog.value.setFormData()
}}</el-button> editAttendanceDialog.value.showDialog = true
</template> }
</el-table-column>
</el-table> /**
<div class="mt-[16px] flex justify-end"> * 编辑考勤
<el-pagination * @param data
v-model:current-page="attendanceTable.page" */
v-model:page-size="attendanceTable.limit" const editEvent = (data: any) => {
layout="total, sizes, prev, pager, next, jumper" editAttendanceDialog.value.setFormData(data)
:total="attendanceTable.total" editAttendanceDialog.value.showDialog = true
@size-change="loadAttendanceList()" }
@current-change="loadAttendanceList"
/> /**
</div> * 删除考勤
</div> */
const deleteEvent = (id: number) => {
<edit ref="editAttendanceDialog" @complete="loadAttendanceList" /> ElMessageBox.confirm(t('attendanceDeleteTips'), t('warning'),
</el-card> {
</div> confirmButtonText: t('confirm'),
</template> cancelButtonText: t('cancel'),
type: 'warning',
<script lang="ts" setup> }
import { reactive, ref, watch } from 'vue' ).then(() => {
import { t } from '@/lang' deleteAttendance(id).then(() => {
import { useDictionary } from '@/app/api/dict' loadAttendanceList()
import { getAttendanceList, deleteAttendance } from '@/app/api/attendance' }).catch(() => {
import { img } from '@/utils/common' })
import { ElMessageBox, FormInstance } from 'element-plus' })
import Edit from '@/app/views/attendance/components/attendance-edit.vue' }
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title const campusIdList = ref([])
const setCampusIdList = async () => {
let attendanceTable = reactive({ campusIdList.value = await (await getWithCampusList({})).data
page: 1, }
limit: 10, setCampusIdList()
total: 0, const staffIdList = ref([])
loading: true, const setStaffIdList = async () => {
data: [], staffIdList.value = await (await getWithPersonnelList({})).data
searchParam: { }
campus_id: '', setStaffIdList()
staff_id: '',
attendance_date: '', const resetForm = (formEl: FormInstance | undefined) => {
check_in_time: '', if (!formEl) return
check_out_time: '', formEl.resetFields()
status: '', loadAttendanceList()
remarks: '', }
created_at: '', </script>
updated_at: '',
coordinate: '', <style lang="scss" scoped>
}, /* 多行超出隐藏 */
}) .multi-hidden {
word-break: break-all;
const searchFormRef = ref<FormInstance>() text-overflow: ellipsis;
overflow: hidden;
// display: -webkit-box;
const selectData = ref<any[]>([]) -webkit-line-clamp: 2;
-webkit-box-orient: vertical;
// }
</style>
/**
* 获取考勤列表
*/
const loadAttendanceList = (page: number = 1) => {
attendanceTable.loading = true
attendanceTable.page = page
getAttendanceList({
page: attendanceTable.page,
limit: attendanceTable.limit,
...attendanceTable.searchParam,
})
.then((res) => {
attendanceTable.loading = false
attendanceTable.data = res.data.data
attendanceTable.total = res.data.total
})
.catch(() => {
attendanceTable.loading = false
})
}
loadAttendanceList()
const editAttendanceDialog: Record<string, any> | null = ref(null)
/**
* 添加考勤
*/
const addEvent = () => {
editAttendanceDialog.value.setFormData()
editAttendanceDialog.value.showDialog = true
}
/**
* 编辑考勤
* @param data
*/
const editEvent = (data: any) => {
editAttendanceDialog.value.setFormData(data)
editAttendanceDialog.value.showDialog = true
}
/**
* 删除考勤
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('attendanceDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteAttendance(id)
.then(() => {
loadAttendanceList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadAttendanceList()
}
</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>

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

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

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;
} }

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

@ -1,274 +1,210 @@
<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> </template>
<el-form-item :label="t('remarks')"> <script lang="ts" setup>
<el-input import { ref, reactive, computed, watch } from 'vue'
v-model="formData.remarks" import { useDictionary } from '@/app/api/dict'
clearable import { t } from '@/lang'
:placeholder="t('remarksPlaceholder')" import type { FormInstance } from 'element-plus'
class="input-width" import { addContract, editContract, getContractInfo } from '@/app/api/contract'
/>
</el-form-item> let showDialog = ref(false)
const loading = ref(false)
<el-form-item :label="t('createdAt')">
<el-input /**
v-model="formData.created_at" * 表单数据
clearable */
:placeholder="t('createdAtPlaceholder')" const initialFormData = {
class="input-width" id: '',
/> contract_name: '',
</el-form-item> contract_template: '',
contract_status: '',
<el-form-item :label="t('updatedAt')"> contract_type: '',
<el-input remarks: '',
v-model="formData.updated_at" }
clearable const formData: Record<string, any> = reactive({ ...initialFormData })
:placeholder="t('updatedAtPlaceholder')"
class="input-width" const formRef = ref<FormInstance>()
/>
</el-form-item> //
const formRules = computed(() => {
<el-form-item :label="t('deletedAt')"> return {
<el-input contract_name: [
v-model="formData.deleted_at" { required: true, message: t('contractNamePlaceholder'), trigger: 'blur' },
clearable
:placeholder="t('deletedAtPlaceholder')" ]
class="input-width" ,
/> contract_template: [
</el-form-item> { required: true, message: t('contractTemplatePlaceholder'), trigger: 'blur' },
</el-form>
]
<template #footer> ,
<span class="dialog-footer"> contract_status: [
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button> { required: true, message: t('contractStatusPlaceholder'), trigger: 'blur' },
<el-button
type="primary" ]
:loading="loading" ,
@click="confirm(formRef)" contract_type: [
>{{ t('confirm') }}</el-button { required: true, message: t('contractTypePlaceholder'), trigger: 'blur' },
>
</span> ]
</template> ,
</el-dialog> remarks: [
</template> { required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
<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 { addContract, editContract, getContractInfo } from '@/app/api/contract' const emit = defineEmits(['complete'])
let showDialog = ref(false) /**
const loading = ref(false) * 确认
* @param formEl
/** */
* 表单数据 const confirm = async (formEl: FormInstance | undefined) => {
*/ if (loading.value || !formEl) return
const initialFormData = { let save = formData.id ? editContract : addContract
id: '',
contract_name: '', await formEl.validate(async (valid) => {
contract_template: '', if (valid) {
contract_status: '', loading.value = true
contract_type: '',
remarks: '', let data = formData
created_at: '',
updated_at: '', save(data).then(res => {
deleted_at: '', loading.value = false
} showDialog.value = false
const formData: Record<string, any> = reactive({ ...initialFormData }) emit('complete')
}).catch(err => {
const formRef = ref<FormInstance>() loading.value = false
})
// }
const formRules = computed(() => { })
return { }
contract_name: [
{ //
required: true, let contract_statusList = ref([])
message: t('contractNamePlaceholder'), const contract_statusDictList = async () => {
trigger: 'blur', contract_statusList.value = await (await useDictionary('ht_status')).data.dictionary
}, }
], contract_statusDictList();
contract_template: [ watch(() => contract_statusList.value, () => { formData.contract_status = contract_statusList.value[0].value })
{ let contract_typeList = ref([])
required: true, const contract_typeDictList = async () => {
message: t('contractTemplatePlaceholder'), contract_typeList.value = await (await useDictionary('ht_type')).data.dictionary
trigger: 'blur', }
}, contract_typeDictList();
], watch(() => contract_typeList.value, () => { formData.contract_type = contract_typeList.value[0].value })
contract_status: [
{
required: true, const setFormData = async (row: any = null) => {
message: t('contractStatusPlaceholder'), Object.assign(formData, initialFormData)
trigger: 'blur', loading.value = true
}, if(row){
], const data = await (await getContractInfo(row.id)).data
contract_type: [ if (data) Object.keys(formData).forEach((key: string) => {
{ if (data[key] != undefined) formData[key] = data[key]
required: true, })
message: t('contractTypePlaceholder'), }
trigger: 'blur', loading.value = false
}, }
],
remarks: [ //
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' }, const mobileVerify = (rule: any, value: any, callback: any) => {
], if (value && !/^1[3-9]\d{9}$/.test(value)) {
created_at: [ callback(new Error(t('generateMobile')))
{ required: true, message: t('createdAtPlaceholder'), trigger: 'blur' }, } else {
], callback()
updated_at: [ }
{ required: true, message: t('updatedAtPlaceholder'), trigger: 'blur' }, }
],
deleted_at: [ //
{ required: true, message: t('deletedAtPlaceholder'), trigger: 'blur' }, 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 emit = defineEmits(['complete']) }
}
/**
* 确认 //
* @param formEl const emailVerify = (rule: any, value: any, callback: any) => {
*/ if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
const confirm = async (formEl: FormInstance | undefined) => { callback(new Error(t('generateEmail')))
if (loading.value || !formEl) return } else {
let save = formData.id ? editContract : addContract callback()
}
await formEl.validate(async (valid) => { }
if (valid) {
loading.value = true //
const numberVerify = (rule: any, value: any, callback: any) => {
let data = formData if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
save(data) } else {
.then((res) => { callback()
loading.value = false }
showDialog.value = false }
emit('complete')
}) defineExpose({
.catch((err) => { showDialog,
loading.value = false setFormData
}) })
} </script>
})
} <style lang="scss" scoped></style>
<style lang="scss">
// .diy-dialog-wrap .el-form-item__label{
height: auto !important;
const setFormData = async (row: any = null) => { }
Object.assign(formData, initialFormData) </style>
loading.value = true
if (row) {
const data = await (await getContractInfo(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>

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

@ -1,290 +1,216 @@
<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>
</template>
<el-table-column
prop="contract_template" <script lang="ts" setup>
:label="t('contractTemplate')" import { reactive, ref, watch } from 'vue'
min-width="120" import { t } from '@/lang'
:show-overflow-tooltip="true" import { useDictionary } from '@/app/api/dict'
/> import { getContractList, deleteContract } from '@/app/api/contract'
import { img } from '@/utils/common'
<el-table-column import { ElMessageBox,FormInstance } from 'element-plus'
prop="contract_status" import Edit from '@/app/views/contract/components/contract-edit.vue'
:label="t('contractStatus')" import { useRoute } from 'vue-router'
min-width="120" const route = useRoute()
:show-overflow-tooltip="true" const pageName = route.meta.title;
/>
let contractTable = reactive({
<el-table-column page: 1,
prop="contract_type" limit: 10,
:label="t('contractType')" total: 0,
min-width="120" loading: true,
:show-overflow-tooltip="true" data: [],
/> searchParam:{
"contract_status":"",
<el-table-column "contract_type":"",
prop="remarks" "created_at":[]
:label="t('remarks')" }
min-width="120" })
:show-overflow-tooltip="true"
/> const searchFormRef = ref<FormInstance>()
<el-table-column //
prop="created_at" const selectData = ref<any[]>([])
:label="t('createdAt')"
min-width="120" //
:show-overflow-tooltip="true" const contract_statusList = ref([] as any[])
/> const contract_statusDictList = async () => {
contract_statusList.value = await (await useDictionary('ht_status')).data.dictionary
<el-table-column }
prop="updated_at" contract_statusDictList();
:label="t('updatedAt')" const contract_typeList = ref([] as any[])
min-width="120" const contract_typeDictList = async () => {
:show-overflow-tooltip="true" contract_typeList.value = await (await useDictionary('ht_type')).data.dictionary
/> }
contract_typeDictList();
<el-table-column
prop="deleted_at" /**
:label="t('deletedAt')" * 获取合同列表
min-width="120" */
:show-overflow-tooltip="true" const loadContractList = (page: number = 1) => {
/> contractTable.loading = true
contractTable.page = page
<el-table-column
:label="t('operation')" getContractList({
fixed="right" page: contractTable.page,
min-width="120" limit: contractTable.limit,
> ...contractTable.searchParam
<template #default="{ row }"> }).then(res => {
<el-button type="primary" link @click="editEvent(row)">{{ contractTable.loading = false
t('edit') contractTable.data = res.data.data
}}</el-button> contractTable.total = res.data.total
<el-button type="primary" link @click="deleteEvent(row.id)">{{ }).catch(() => {
t('delete') contractTable.loading = false
}}</el-button> })
</template> }
</el-table-column> loadContractList()
</el-table>
<div class="mt-[16px] flex justify-end"> const editContractDialog: Record<string, any> | null = ref(null)
<el-pagination
v-model:current-page="contractTable.page" /**
v-model:page-size="contractTable.limit" * 添加合同
layout="total, sizes, prev, pager, next, jumper" */
:total="contractTable.total" const addEvent = () => {
@size-change="loadContractList()" editContractDialog.value.setFormData()
@current-change="loadContractList" editContractDialog.value.showDialog = true
/> }
</div>
</div> /**
* 编辑合同
<edit ref="editContractDialog" @complete="loadContractList" /> * @param data
</el-card> */
</div> const editEvent = (data: any) => {
</template> editContractDialog.value.setFormData(data)
editContractDialog.value.showDialog = true
<script lang="ts" setup> }
import { reactive, ref, watch } from 'vue'
import { t } from '@/lang' /**
import { useDictionary } from '@/app/api/dict' * 删除合同
import { getContractList, deleteContract } from '@/app/api/contract' */
import { img } from '@/utils/common' const deleteEvent = (id: number) => {
import { ElMessageBox, FormInstance } from 'element-plus' ElMessageBox.confirm(t('contractDeleteTips'), t('warning'),
import Edit from '@/app/views/contract/components/contract-edit.vue' {
import { useRoute } from 'vue-router' confirmButtonText: t('confirm'),
const route = useRoute() cancelButtonText: t('cancel'),
const pageName = route.meta.title type: 'warning',
}
let contractTable = reactive({ ).then(() => {
page: 1, deleteContract(id).then(() => {
limit: 10, loadContractList()
total: 0, }).catch(() => {
loading: true, })
data: [], })
searchParam: { }
contract_name: '',
contract_template: '',
contract_status: '',
contract_type: '', const resetForm = (formEl: FormInstance | undefined) => {
remarks: '', if (!formEl) return
created_at: '', formEl.resetFields()
updated_at: '', loadContractList()
deleted_at: '', }
}, </script>
})
<style lang="scss" scoped>
const searchFormRef = ref<FormInstance>() /* 多行超出隐藏 */
.multi-hidden {
// word-break: break-all;
const selectData = ref<any[]>([]) text-overflow: ellipsis;
overflow: hidden;
// display: -webkit-box;
-webkit-line-clamp: 2;
/** -webkit-box-orient: vertical;
* 获取合同列表 }
*/ </style>
const loadContractList = (page: number = 1) => {
contractTable.loading = true
contractTable.page = page
getContractList({
page: contractTable.page,
limit: contractTable.limit,
...contractTable.searchParam,
})
.then((res) => {
contractTable.loading = false
contractTable.data = res.data.data
contractTable.total = res.data.total
})
.catch(() => {
contractTable.loading = false
})
}
loadContractList()
const editContractDialog: Record<string, any> | null = ref(null)
/**
* 添加合同
*/
const addEvent = () => {
editContractDialog.value.setFormData()
editContractDialog.value.showDialog = true
}
/**
* 编辑合同
* @param data
*/
const editEvent = (data: any) => {
editContractDialog.value.setFormData(data)
editContractDialog.value.showDialog = true
}
/**
* 删除合同
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('contractDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteContract(id)
.then(() => {
loadContractList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadContractList()
}
</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>

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

@ -1,223 +1,233 @@
<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> </template>
<el-button
type="primary" <script lang="ts" setup>
:loading="loading" import { ref, reactive, computed, watch } from 'vue'
@click="confirm(formRef)" import { useDictionary } from '@/app/api/dict'
>{{ t('confirm') }}</el-button import { t } from '@/lang'
> import type { FormInstance } from 'element-plus'
</span> import { addExamAnswers, editExamAnswers, getExamAnswersInfo, getWithCampusList, getWithPersonnelList, getWithExamQuestionsList } from '@/app/api/exam_answers'
</template>
</el-dialog> let showDialog = ref(false)
</template> const loading = ref(false)
<script lang="ts" setup> /**
import { ref, reactive, computed, watch } from 'vue' * 表单数据
import { useDictionary } from '@/app/api/dict' */
import { t } from '@/lang' const initialFormData = {
import type { FormInstance } from 'element-plus' id: '',
import { campus_id: '',
addExamAnswers, user_id: '',
editExamAnswers, question_id: '',
getExamAnswersInfo, answer: '',
} from '@/app/api/exam_answers' is_correct: '',
}
let showDialog = ref(false) const formData: Record<string, any> = reactive({ ...initialFormData })
const loading = ref(false)
const formRef = ref<FormInstance>()
/**
* 表单数据 //
*/ const formRules = computed(() => {
const initialFormData = { return {
id: '', campus_id: [
campus_id: '', { required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
user_id: '',
question_id: '', ]
answer: '', ,
is_correct: '', user_id: [
} { required: true, message: t('userIdPlaceholder'), trigger: 'blur' },
const formData: Record<string, any> = reactive({ ...initialFormData })
]
const formRef = ref<FormInstance>() ,
question_id: [
// { required: true, message: t('questionIdPlaceholder'), trigger: 'blur' },
const formRules = computed(() => {
return { ]
campus_id: [ ,
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' }, answer: [
], { required: true, message: t('answerPlaceholder'), trigger: 'blur' },
user_id: [
{ required: true, message: t('userIdPlaceholder'), trigger: 'blur' }, ]
], ,
question_id: [ is_correct: [
{ required: true, message: t('questionIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('isCorrectPlaceholder'), trigger: 'blur' },
],
answer: [ ]
{ required: true, message: t('answerPlaceholder'), trigger: 'blur' }, ,
], }
is_correct: [ })
{ required: true, message: t('isCorrectPlaceholder'), trigger: 'blur' },
], const emit = defineEmits(['complete'])
}
}) /**
* 确认
const emit = defineEmits(['complete']) * @param formEl
*/
/** const confirm = async (formEl: FormInstance | undefined) => {
* 确认 if (loading.value || !formEl) return
* @param formEl let save = formData.id ? editExamAnswers : addExamAnswers
*/
const confirm = async (formEl: FormInstance | undefined) => { await formEl.validate(async (valid) => {
if (loading.value || !formEl) return if (valid) {
let save = formData.id ? editExamAnswers : addExamAnswers loading.value = true
await formEl.validate(async (valid) => { let data = formData
if (valid) {
loading.value = true save(data).then(res => {
loading.value = false
let data = formData showDialog.value = false
emit('complete')
save(data) }).catch(err => {
.then((res) => { loading.value = false
loading.value = false })
showDialog.value = false }
emit('complete') })
}) }
.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 setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData) const campusIdList = ref([] as any[])
loading.value = true const setCampusIdList = async () => {
if (row) { campusIdList.value = await (await getWithCampusList({})).data
const data = await (await getExamAnswersInfo(row.id)).data }
if (data) setCampusIdList()
Object.keys(formData).forEach((key: string) => { const userIdList = ref([] as any[])
if (data[key] != undefined) formData[key] = data[key] const setUserIdList = async () => {
}) userIdList.value = await (await getWithPersonnelList({})).data
} }
loading.value = false setUserIdList()
} const questionIdList = ref([] as any[])
const setQuestionIdList = async () => {
// questionIdList.value = await (await getWithExamQuestionsList({})).data
const mobileVerify = (rule: any, value: any, callback: any) => { }
if (value && !/^1[3-9]\d{9}$/.test(value)) { setQuestionIdList()
callback(new Error(t('generateMobile'))) const setFormData = async (row: any = null) => {
} else { Object.assign(formData, initialFormData)
callback() loading.value = true
} if(row){
} const data = await (await getExamAnswersInfo(row.id)).data
if (data) Object.keys(formData).forEach((key: string) => {
// if (data[key] != undefined) formData[key] = data[key]
const idCardVerify = (rule: any, value: any, callback: any) => { })
if ( }
value && loading.value = false
!/^[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
) //
) { const mobileVerify = (rule: any, value: any, callback: any) => {
callback(new Error(t('generateIdCard'))) if (value && !/^1[3-9]\d{9}$/.test(value)) {
} else { callback(new Error(t('generateMobile')))
callback() } else {
} callback()
} }
}
//
const emailVerify = (rule: any, value: any, callback: any) => { //
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { const idCardVerify = (rule: any, value: any, callback: any) => {
callback(new Error(t('generateEmail'))) 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)) {
} else { callback(new Error(t('generateIdCard')))
callback() } else {
} callback()
} }
}
//
const numberVerify = (rule: any, value: any, callback: any) => { //
if (!Number.isInteger(value)) { const emailVerify = (rule: any, value: any, callback: any) => {
callback(new Error(t('generateNumber'))) if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
} else { callback(new Error(t('generateEmail')))
callback() } else {
} callback()
} }
}
defineExpose({
showDialog, //
setFormData, const numberVerify = (rule: any, value: any, callback: any) => {
}) if (!Number.isInteger(value)) {
</script> callback(new Error(t('generateNumber')))
} else {
<style lang="scss" scoped></style> callback()
<style lang="scss"> }
.diy-dialog-wrap .el-form-item__label { }
height: auto !important;
} defineExpose({
</style> showDialog,
setFormData
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label{
height: auto !important;
}
</style>

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

@ -1,248 +1,203 @@
<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" </template>
:show-overflow-tooltip="true"
/> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
<el-table-column import { t } from '@/lang'
prop="user_id" import { useDictionary } from '@/app/api/dict'
:label="t('userId')" import { getExamAnswersList, deleteExamAnswers, getWithCampusList, getWithPersonnelList, getWithExamQuestionsList } from '@/app/api/exam_answers'
min-width="120" import { img } from '@/utils/common'
:show-overflow-tooltip="true" import { ElMessageBox,FormInstance } from 'element-plus'
/> import Edit from '@/app/views/exam_answers/components/exam-answers-edit.vue'
import { useRoute } from 'vue-router'
<el-table-column const route = useRoute()
prop="question_id" const pageName = route.meta.title;
:label="t('questionId')"
min-width="120" let examAnswersTable = reactive({
:show-overflow-tooltip="true" page: 1,
/> limit: 10,
total: 0,
<el-table-column loading: true,
prop="answer" data: [],
:label="t('answer')" searchParam:{
min-width="120" "campus_id":""
:show-overflow-tooltip="true" }
/> })
<el-table-column const searchFormRef = ref<FormInstance>()
prop="is_correct"
:label="t('isCorrect')" //
min-width="120" const selectData = ref<any[]>([])
:show-overflow-tooltip="true"
/> //
const is_correctList = ref([] as any[])
<el-table-column const is_correctDictList = async () => {
:label="t('operation')" is_correctList.value = await (await useDictionary('is_correct')).data.dictionary
fixed="right" }
min-width="120" is_correctDictList();
>
<template #default="{ row }"> /**
<el-button type="primary" link @click="editEvent(row)">{{ * 获取答题记录列表
t('edit') */
}}</el-button> const loadExamAnswersList = (page: number = 1) => {
<el-button type="primary" link @click="deleteEvent(row.id)">{{ examAnswersTable.loading = true
t('delete') examAnswersTable.page = page
}}</el-button>
</template> getExamAnswersList({
</el-table-column> page: examAnswersTable.page,
</el-table> limit: examAnswersTable.limit,
<div class="mt-[16px] flex justify-end"> ...examAnswersTable.searchParam
<el-pagination }).then(res => {
v-model:current-page="examAnswersTable.page" examAnswersTable.loading = false
v-model:page-size="examAnswersTable.limit" examAnswersTable.data = res.data.data
layout="total, sizes, prev, pager, next, jumper" examAnswersTable.total = res.data.total
:total="examAnswersTable.total" }).catch(() => {
@size-change="loadExamAnswersList()" examAnswersTable.loading = false
@current-change="loadExamAnswersList" })
/> }
</div> loadExamAnswersList()
</div>
const editExamAnswersDialog: Record<string, any> | null = ref(null)
<edit ref="editExamAnswersDialog" @complete="loadExamAnswersList" />
</el-card> /**
</div> * 添加答题记录
</template> */
const addEvent = () => {
<script lang="ts" setup> editExamAnswersDialog.value.setFormData()
import { reactive, ref, watch } from 'vue' editExamAnswersDialog.value.showDialog = true
import { t } from '@/lang' }
import { useDictionary } from '@/app/api/dict'
import { getExamAnswersList, deleteExamAnswers } from '@/app/api/exam_answers' /**
import { img } from '@/utils/common' * 编辑答题记录
import { ElMessageBox, FormInstance } from 'element-plus' * @param data
import Edit from '@/app/views/exam_answers/components/exam-answers-edit.vue' */
import { useRoute } from 'vue-router' const editEvent = (data: any) => {
const route = useRoute() editExamAnswersDialog.value.setFormData(data)
const pageName = route.meta.title editExamAnswersDialog.value.showDialog = true
}
let examAnswersTable = reactive({
page: 1, /**
limit: 10, * 删除答题记录
total: 0, */
loading: true, const deleteEvent = (id: number) => {
data: [], ElMessageBox.confirm(t('examAnswersDeleteTips'), t('warning'),
searchParam: { {
campus_id: '', confirmButtonText: t('confirm'),
user_id: '', cancelButtonText: t('cancel'),
question_id: '', type: 'warning',
answer: '', }
is_correct: '', ).then(() => {
}, deleteExamAnswers(id).then(() => {
}) loadExamAnswersList()
}).catch(() => {
const searchFormRef = ref<FormInstance>() })
})
// }
const selectData = ref<any[]>([])
// const campusIdList = ref([])
const setCampusIdList = async () => {
/** campusIdList.value = await (await getWithCampusList({})).data
* 获取答题记录列表 }
*/ setCampusIdList()
const loadExamAnswersList = (page: number = 1) => { const userIdList = ref([])
examAnswersTable.loading = true const setUserIdList = async () => {
examAnswersTable.page = page userIdList.value = await (await getWithPersonnelList({})).data
}
getExamAnswersList({ setUserIdList()
page: examAnswersTable.page, const questionIdList = ref([])
limit: examAnswersTable.limit, const setQuestionIdList = async () => {
...examAnswersTable.searchParam, questionIdList.value = await (await getWithExamQuestionsList({})).data
}) }
.then((res) => { setQuestionIdList()
examAnswersTable.loading = false
examAnswersTable.data = res.data.data const resetForm = (formEl: FormInstance | undefined) => {
examAnswersTable.total = res.data.total if (!formEl) return
}) formEl.resetFields()
.catch(() => { loadExamAnswersList()
examAnswersTable.loading = false }
}) </script>
}
loadExamAnswersList() <style lang="scss" scoped>
/* 多行超出隐藏 */
const editExamAnswersDialog: Record<string, any> | null = ref(null) .multi-hidden {
word-break: break-all;
/** text-overflow: ellipsis;
* 添加答题记录 overflow: hidden;
*/ display: -webkit-box;
const addEvent = () => { -webkit-line-clamp: 2;
editExamAnswersDialog.value.setFormData() -webkit-box-orient: vertical;
editExamAnswersDialog.value.showDialog = true }
} </style>
/**
* 编辑答题记录
* @param data
*/
const editEvent = (data: any) => {
editExamAnswersDialog.value.setFormData(data)
editExamAnswersDialog.value.showDialog = true
}
/**
* 删除答题记录
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('examAnswersDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteExamAnswers(id)
.then(() => {
loadExamAnswersList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadExamAnswersList()
}
</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>

487
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>

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

@ -1,220 +1,195 @@
<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 </template>
:label="t('operation')"
fixed="right" <script lang="ts" setup>
min-width="120" import { reactive, ref, watch } from 'vue'
> import { t } from '@/lang'
<template #default="{ row }"> import { useDictionary } from '@/app/api/dict'
<el-button type="primary" link @click="editEvent(row)">{{ import { getExamPapersList, deleteExamPapers } from '@/app/api/exam_papers'
t('edit') import { img } from '@/utils/common'
}}</el-button> import { ElMessageBox,FormInstance } from 'element-plus'
<el-button type="primary" link @click="deleteEvent(row.id)">{{ import Edit from '@/app/views/exam_papers/components/exam-papers-edit.vue'
t('delete') import { useRoute } from 'vue-router'
}}</el-button> const route = useRoute()
</template> const pageName = route.meta.title;
</el-table-column>
</el-table> let examPapersTable = reactive({
<div class="mt-[16px] flex justify-end"> page: 1,
<el-pagination limit: 10,
v-model:current-page="examPapersTable.page" total: 0,
v-model:page-size="examPapersTable.limit" loading: true,
layout="total, sizes, prev, pager, next, jumper" data: [],
:total="examPapersTable.total" searchParam:{
@size-change="loadExamPapersList()" "selection_mode":"",
@current-change="loadExamPapersList" "created_at":[]
/> }
</div> })
</div>
const searchFormRef = ref<FormInstance>()
<edit ref="editExamPapersDialog" @complete="loadExamPapersList" />
</el-card> //
</div> const selectData = ref<any[]>([])
</template>
//
<script lang="ts" setup> const selection_modeList = ref([] as any[])
import { reactive, ref, watch } from 'vue' const selection_modeDictList = async () => {
import { t } from '@/lang' selection_modeList.value = await (await useDictionary('selection_mode')).data.dictionary
import { useDictionary } from '@/app/api/dict' }
import { getExamPapersList, deleteExamPapers } from '@/app/api/exam_papers' selection_modeDictList();
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus' /**
import Edit from '@/app/views/exam_papers/components/exam-papers-edit.vue' * 获取试卷列表
import { useRoute } from 'vue-router' */
const route = useRoute() const loadExamPapersList = (page: number = 1) => {
const pageName = route.meta.title examPapersTable.loading = true
examPapersTable.page = page
let examPapersTable = reactive({
page: 1, getExamPapersList({
limit: 10, page: examPapersTable.page,
total: 0, limit: examPapersTable.limit,
loading: true, ...examPapersTable.searchParam
data: [], }).then(res => {
searchParam: { examPapersTable.loading = false
selection_mode: '', examPapersTable.data = res.data.data
total_score: '', examPapersTable.total = res.data.total
passing_score: '', }).catch(() => {
}, examPapersTable.loading = false
}) })
}
const searchFormRef = ref<FormInstance>() loadExamPapersList()
// const editExamPapersDialog: Record<string, any> | null = ref(null)
const selectData = ref<any[]>([])
/**
// * 添加试卷
*/
/** const addEvent = () => {
* 获取试卷列表 editExamPapersDialog.value.setFormData()
*/ editExamPapersDialog.value.showDialog = true
const loadExamPapersList = (page: number = 1) => { }
examPapersTable.loading = true
examPapersTable.page = page /**
* 编辑试卷
getExamPapersList({ * @param data
page: examPapersTable.page, */
limit: examPapersTable.limit, const editEvent = (data: any) => {
...examPapersTable.searchParam, editExamPapersDialog.value.setFormData(data)
}) editExamPapersDialog.value.showDialog = true
.then((res) => { }
examPapersTable.loading = false
examPapersTable.data = res.data.data /**
examPapersTable.total = res.data.total * 删除试卷
}) */
.catch(() => { const deleteEvent = (id: number) => {
examPapersTable.loading = false ElMessageBox.confirm(t('examPapersDeleteTips'), t('warning'),
}) {
} confirmButtonText: t('confirm'),
loadExamPapersList() cancelButtonText: t('cancel'),
type: 'warning',
const editExamPapersDialog: Record<string, any> | null = ref(null) }
).then(() => {
/** deleteExamPapers(id).then(() => {
* 添加试卷 loadExamPapersList()
*/ }).catch(() => {
const addEvent = () => { })
editExamPapersDialog.value.setFormData() })
editExamPapersDialog.value.showDialog = true }
}
/**
* 编辑试卷 const resetForm = (formEl: FormInstance | undefined) => {
* @param data if (!formEl) return
*/ formEl.resetFields()
const editEvent = (data: any) => { loadExamPapersList()
editExamPapersDialog.value.setFormData(data) }
editExamPapersDialog.value.showDialog = true </script>
}
<style lang="scss" scoped>
/** /* 多行超出隐藏 */
* 删除试卷 .multi-hidden {
*/ word-break: break-all;
const deleteEvent = (id: number) => { text-overflow: ellipsis;
ElMessageBox.confirm(t('examPapersDeleteTips'), t('warning'), { overflow: hidden;
confirmButtonText: t('confirm'), display: -webkit-box;
cancelButtonText: t('cancel'), -webkit-line-clamp: 2;
type: 'warning', -webkit-box-orient: vertical;
}).then(() => { }
deleteExamPapers(id) </style>
.then(() => {
loadExamPapersList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadExamPapersList()
}
</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>

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>

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

@ -1,360 +1,216 @@
<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"> </template>
<el-input
v-model="examQuestionsTable.searchParam.option_d_content" <script lang="ts" setup>
:placeholder="t('optionDContentPlaceholder')" import { reactive, ref, watch } from 'vue'
/> import { t } from '@/lang'
</el-form-item> import { useDictionary } from '@/app/api/dict'
<el-form-item :label="t('correctAnswer')" prop="correct_answer"> import { getExamQuestionsList, deleteExamQuestions } from '@/app/api/exam_questions'
<el-input import { img } from '@/utils/common'
v-model="examQuestionsTable.searchParam.correct_answer" import { ElMessageBox,FormInstance } from 'element-plus'
:placeholder="t('correctAnswerPlaceholder')" import Edit from '@/app/views/exam_questions/components/exam-questions-edit.vue'
/> import { useRoute } from 'vue-router'
</el-form-item> const route = useRoute()
const pageName = route.meta.title;
<el-form-item>
<el-button type="primary" @click="loadExamQuestionsList()">{{ let examQuestionsTable = reactive({
t('search') page: 1,
}}</el-button> limit: 10,
<el-button @click="resetForm(searchFormRef)">{{ total: 0,
t('reset') loading: true,
}}</el-button> data: [],
</el-form-item> searchParam:{
</el-form> "title":"",
</el-card> "question_type":"",
"created_at":[]
<div class="mt-[10px]"> }
<el-table })
:data="examQuestionsTable.data"
size="large" const searchFormRef = ref<FormInstance>()
v-loading="examQuestionsTable.loading"
> //
<template #empty> const selectData = ref<any[]>([])
<span>{{ !examQuestionsTable.loading ? t('emptyData') : '' }}</span>
</template> //
<el-table-column const question_typeList = ref([] as any[])
prop="question_type" const question_typeDictList = async () => {
:label="t('questionType')" question_typeList.value = await (await useDictionary('question_type')).data.dictionary
min-width="120" }
:show-overflow-tooltip="true" question_typeDictList();
/> const question_content_typeList = ref([] as any[])
const question_content_typeDictList = async () => {
<el-table-column question_content_typeList.value = await (await useDictionary('question_content_type')).data.dictionary
prop="question_content_type" }
:label="t('questionContentType')" question_content_typeDictList();
min-width="120"
:show-overflow-tooltip="true" /**
/> * 获取试题列表
*/
<el-table-column const loadExamQuestionsList = (page: number = 1) => {
prop="question_content" examQuestionsTable.loading = true
:label="t('questionContent')" examQuestionsTable.page = page
min-width="120"
:show-overflow-tooltip="true" getExamQuestionsList({
/> page: examQuestionsTable.page,
limit: examQuestionsTable.limit,
<el-table-column ...examQuestionsTable.searchParam
prop="option_a_content_type" }).then(res => {
:label="t('optionAContentType')" examQuestionsTable.loading = false
min-width="120" examQuestionsTable.data = res.data.data
:show-overflow-tooltip="true" examQuestionsTable.total = res.data.total
/> }).catch(() => {
examQuestionsTable.loading = false
<el-table-column })
prop="option_a_content" }
:label="t('optionAContent')" loadExamQuestionsList()
min-width="120"
:show-overflow-tooltip="true" const editExamQuestionsDialog: Record<string, any> | null = ref(null)
/>
/**
<el-table-column * 添加试题
prop="option_b_content_type" */
:label="t('optionBContentType')" const addEvent = () => {
min-width="120" editExamQuestionsDialog.value.setFormData()
:show-overflow-tooltip="true" editExamQuestionsDialog.value.showDialog = true
/> }
<el-table-column /**
prop="option_b_content" * 编辑试题
:label="t('optionBContent')" * @param data
min-width="120" */
:show-overflow-tooltip="true" const editEvent = (data: any) => {
/> editExamQuestionsDialog.value.setFormData(data)
editExamQuestionsDialog.value.showDialog = true
<el-table-column }
prop="option_c_content_type"
:label="t('optionCContentType')" /**
min-width="120" * 删除试题
:show-overflow-tooltip="true" */
/> const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('examQuestionsDeleteTips'), t('warning'),
<el-table-column {
prop="option_c_content" confirmButtonText: t('confirm'),
:label="t('optionCContent')" cancelButtonText: t('cancel'),
min-width="120" type: 'warning',
:show-overflow-tooltip="true" }
/> ).then(() => {
deleteExamQuestions(id).then(() => {
<el-table-column loadExamQuestionsList()
prop="option_d_content_type" }).catch(() => {
:label="t('optionDContentType')" })
min-width="120" })
:show-overflow-tooltip="true" }
/>
<el-table-column
prop="option_d_content" const resetForm = (formEl: FormInstance | undefined) => {
:label="t('optionDContent')" if (!formEl) return
min-width="120" formEl.resetFields()
:show-overflow-tooltip="true" loadExamQuestionsList()
/> }
</script>
<el-table-column
prop="correct_answer" <style lang="scss" scoped>
:label="t('correctAnswer')" /* 多行超出隐藏 */
min-width="120" .multi-hidden {
:show-overflow-tooltip="true" word-break: break-all;
/> text-overflow: ellipsis;
overflow: hidden;
<el-table-column display: -webkit-box;
:label="t('operation')" -webkit-line-clamp: 2;
fixed="right" -webkit-box-orient: vertical;
min-width="120" }
> </style>
<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>
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import {
getExamQuestionsList,
deleteExamQuestions,
} from '@/app/api/exam_questions'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRouter } from 'vue-router'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
let examQuestionsTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
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: '',
},
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
/**
* 获取试题列表
*/
const loadExamQuestionsList = (page: number = 1) => {
examQuestionsTable.loading = true
examQuestionsTable.page = page
getExamQuestionsList({
page: examQuestionsTable.page,
limit: examQuestionsTable.limit,
...examQuestionsTable.searchParam,
})
.then((res) => {
examQuestionsTable.loading = false
examQuestionsTable.data = res.data.data
examQuestionsTable.total = res.data.total
})
.catch(() => {
examQuestionsTable.loading = false
})
}
loadExamQuestionsList()
const router = useRouter()
/**
* 添加试题
*/
const addEvent = () => {
router.push('/exam_questions/exam_questions_edit')
}
/**
* 编辑试题
* @param data
*/
const editEvent = (data: any) => {
router.push('/exam_questions/exam_questions_edit?id=' + data.id)
}
/**
* 删除试题
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('examQuestionsDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteExamQuestions(id)
.then(() => {
loadExamQuestionsList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadExamQuestionsList()
}
</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>

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

@ -1,249 +1,267 @@
<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)" </template>
>{{ t('confirm') }}</el-button
> <script lang="ts" setup>
</span> import { ref, reactive, computed, watch } from 'vue'
</template> import { useDictionary } from '@/app/api/dict'
</el-dialog> import { t } from '@/lang'
</template> import type { FormInstance } from 'element-plus'
import { addExamRecords, editExamRecords, getExamRecordsInfo, getWithCampusList, getWithPersonnelList, getWithExamPapersList } from '@/app/api/exam_records'
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue' let showDialog = ref(false)
import { useDictionary } from '@/app/api/dict' const loading = ref(false)
import { t } from '@/lang'
import type { FormInstance } from 'element-plus' /**
import { * 表单数据
addExamRecords, */
editExamRecords, const initialFormData = {
getExamRecordsInfo, id: '',
} from '@/app/api/exam_records' campus_id: '',
user_id: '',
let showDialog = ref(false) paper_id: '',
const loading = ref(false) score: '',
status: '',
/** start_time: '',
* 表单数据 end_time: '',
*/ }
const initialFormData = { const formData: Record<string, any> = reactive({ ...initialFormData })
id: '',
campus_id: '', const formRef = ref<FormInstance>()
user_id: '',
paper_id: '', //
score: '', const formRules = computed(() => {
status: '', return {
start_time: '', campus_id: [
end_time: '', { required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
}
const formData: Record<string, any> = reactive({ ...initialFormData }) ]
,
const formRef = ref<FormInstance>() user_id: [
{ required: true, message: t('userIdPlaceholder'), trigger: 'blur' },
//
const formRules = computed(() => { ]
return { ,
campus_id: [ paper_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('paperIdPlaceholder'), trigger: 'blur' },
],
user_id: [ ]
{ required: true, message: t('userIdPlaceholder'), trigger: 'blur' }, ,
], score: [
paper_id: [ { required: true, message: t('scorePlaceholder'), trigger: 'blur' },
{ required: true, message: t('paperIdPlaceholder'), trigger: 'blur' }, { validator: (rule: any, value: string, callback: any) => { if (value && !/^\d{0,100}$/.test(value)) { callback(new Error(t('generateBetween')))} else { callback() }}},
], ]
score: [ ,
{ required: true, message: t('scorePlaceholder'), trigger: 'blur' }, status: [
], { required: true, message: t('statusPlaceholder'), trigger: 'blur' },
status: [
{ 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: [ ]
{ required: true, message: t('endTimePlaceholder'), trigger: 'blur' }, ,
], end_time: [
} { required: true, message: t('endTimePlaceholder'), trigger: 'blur' },
})
]
const emit = defineEmits(['complete']) ,
}
/** })
* 确认
* @param formEl const emit = defineEmits(['complete'])
*/
const confirm = async (formEl: FormInstance | undefined) => { /**
if (loading.value || !formEl) return * 确认
let save = formData.id ? editExamRecords : addExamRecords * @param formEl
*/
await formEl.validate(async (valid) => { const confirm = async (formEl: FormInstance | undefined) => {
if (valid) { if (loading.value || !formEl) return
loading.value = true let save = formData.id ? editExamRecords : addExamRecords
let data = formData await formEl.validate(async (valid) => {
if (valid) {
save(data) loading.value = true
.then((res) => {
loading.value = false let data = formData
showDialog.value = false
emit('complete') save(data).then(res => {
}) loading.value = false
.catch((err) => { showDialog.value = false
loading.value = false emit('complete')
}) }).catch(err => {
} loading.value = false
}) })
} }
})
// }
const setFormData = async (row: any = null) => { //
Object.assign(formData, initialFormData) let statusList = ref([])
loading.value = true const statusDictList = async () => {
if (row) { statusList.value = await (await useDictionary('ks_status')).data.dictionary
const data = await (await getExamRecordsInfo(row.id)).data }
if (data) statusDictList();
Object.keys(formData).forEach((key: string) => { watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
if (data[key] != undefined) formData[key] = data[key]
})
} const campusIdList = ref([] as any[])
loading.value = false const setCampusIdList = async () => {
} campusIdList.value = await (await getWithCampusList({})).data
}
// setCampusIdList()
const mobileVerify = (rule: any, value: any, callback: any) => { const userIdList = ref([] as any[])
if (value && !/^1[3-9]\d{9}$/.test(value)) { const setUserIdList = async () => {
callback(new Error(t('generateMobile'))) userIdList.value = await (await getWithPersonnelList({})).data
} else { }
callback() setUserIdList()
} const paperIdList = ref([] as any[])
} const setPaperIdList = async () => {
paperIdList.value = await (await getWithExamPapersList({})).data
// }
const idCardVerify = (rule: any, value: any, callback: any) => { setPaperIdList()
if ( const setFormData = async (row: any = null) => {
value && Object.assign(formData, initialFormData)
!/^[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( loading.value = true
value if(row){
) const data = await (await getExamRecordsInfo(row.id)).data
) { if (data) Object.keys(formData).forEach((key: string) => {
callback(new Error(t('generateIdCard'))) if (data[key] != undefined) formData[key] = data[key]
} else { })
callback() }
} loading.value = false
} }
// //
const emailVerify = (rule: any, value: any, callback: any) => { const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateEmail'))) callback(new Error(t('generateMobile')))
} else { } else {
callback() callback()
} }
} }
// //
const numberVerify = (rule: any, value: any, callback: any) => { const idCardVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) { 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('generateNumber'))) callback(new Error(t('generateIdCard')))
} else { } else {
callback() callback()
} }
} }
defineExpose({ //
showDialog, const emailVerify = (rule: any, value: any, callback: any) => {
setFormData, if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
}) callback(new Error(t('generateEmail')))
</script> } else {
callback()
<style lang="scss" scoped></style> }
<style lang="scss"> }
.diy-dialog-wrap .el-form-item__label {
height: auto !important; //
} const numberVerify = (rule: any, value: any, callback: any) => {
</style> 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>

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

@ -1,276 +1,232 @@
<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" </template>
/>
<script lang="ts" setup>
<el-table-column import { reactive, ref, watch } from 'vue'
prop="score" import { t } from '@/lang'
:label="t('score')" import { useDictionary } from '@/app/api/dict'
min-width="120" import { getExamRecordsList, deleteExamRecords, getWithCampusList, getWithPersonnelList, getWithExamPapersList } from '@/app/api/exam_records'
:show-overflow-tooltip="true" import { img } from '@/utils/common'
/> import { ElMessageBox,FormInstance } from 'element-plus'
import Edit from '@/app/views/exam_records/components/exam-records-edit.vue'
<el-table-column import { useRoute } from 'vue-router'
prop="status" const route = useRoute()
:label="t('status')" const pageName = route.meta.title;
min-width="120"
:show-overflow-tooltip="true" let examRecordsTable = reactive({
/> page: 1,
limit: 10,
<el-table-column total: 0,
prop="start_time" loading: true,
:label="t('startTime')" data: [],
min-width="120" searchParam:{
:show-overflow-tooltip="true" "campus_id":"",
/> "paper_id":"",
"status":""
<el-table-column }
prop="end_time" })
:label="t('endTime')"
min-width="120" const searchFormRef = ref<FormInstance>()
:show-overflow-tooltip="true"
/> //
const selectData = ref<any[]>([])
<el-table-column
:label="t('operation')" //
fixed="right" const statusList = ref([] as any[])
min-width="120" const statusDictList = async () => {
> statusList.value = await (await useDictionary('ks_status')).data.dictionary
<template #default="{ row }"> }
<el-button type="primary" link @click="editEvent(row)">{{ statusDictList();
t('edit')
}}</el-button> /**
<el-button type="primary" link @click="deleteEvent(row.id)">{{ * 获取考试记录列表
t('delete') */
}}</el-button> const loadExamRecordsList = (page: number = 1) => {
</template> examRecordsTable.loading = true
</el-table-column> examRecordsTable.page = page
</el-table>
<div class="mt-[16px] flex justify-end"> getExamRecordsList({
<el-pagination page: examRecordsTable.page,
v-model:current-page="examRecordsTable.page" limit: examRecordsTable.limit,
v-model:page-size="examRecordsTable.limit" ...examRecordsTable.searchParam
layout="total, sizes, prev, pager, next, jumper" }).then(res => {
:total="examRecordsTable.total" examRecordsTable.loading = false
@size-change="loadExamRecordsList()" examRecordsTable.data = res.data.data
@current-change="loadExamRecordsList" examRecordsTable.total = res.data.total
/> }).catch(() => {
</div> examRecordsTable.loading = false
</div> })
}
<edit ref="editExamRecordsDialog" @complete="loadExamRecordsList" /> loadExamRecordsList()
</el-card>
</div> const editExamRecordsDialog: Record<string, any> | null = ref(null)
</template>
/**
<script lang="ts" setup> * 添加考试记录
import { reactive, ref, watch } from 'vue' */
import { t } from '@/lang' const addEvent = () => {
import { useDictionary } from '@/app/api/dict' editExamRecordsDialog.value.setFormData()
import { getExamRecordsList, deleteExamRecords } from '@/app/api/exam_records' editExamRecordsDialog.value.showDialog = true
import { img } from '@/utils/common' }
import { ElMessageBox, FormInstance } from 'element-plus'
import Edit from '@/app/views/exam_records/components/exam-records-edit.vue' /**
import { useRoute } from 'vue-router' * 编辑考试记录
const route = useRoute() * @param data
const pageName = route.meta.title */
const editEvent = (data: any) => {
let examRecordsTable = reactive({ editExamRecordsDialog.value.setFormData(data)
page: 1, editExamRecordsDialog.value.showDialog = true
limit: 10, }
total: 0,
loading: true, /**
data: [], * 删除考试记录
searchParam: { */
campus_id: '', const deleteEvent = (id: number) => {
user_id: '', ElMessageBox.confirm(t('examRecordsDeleteTips'), t('warning'),
paper_id: '', {
score: '', confirmButtonText: t('confirm'),
status: '', cancelButtonText: t('cancel'),
start_time: '', type: 'warning',
end_time: '', }
}, ).then(() => {
}) deleteExamRecords(id).then(() => {
loadExamRecordsList()
const searchFormRef = ref<FormInstance>() }).catch(() => {
})
// })
const selectData = ref<any[]>([]) }
//
const campusIdList = ref([])
/** const setCampusIdList = async () => {
* 获取考试记录列表 campusIdList.value = await (await getWithCampusList({})).data
*/ }
const loadExamRecordsList = (page: number = 1) => { setCampusIdList()
examRecordsTable.loading = true const userIdList = ref([])
examRecordsTable.page = page const setUserIdList = async () => {
userIdList.value = await (await getWithPersonnelList({})).data
getExamRecordsList({ }
page: examRecordsTable.page, setUserIdList()
limit: examRecordsTable.limit, const paperIdList = ref([])
...examRecordsTable.searchParam, const setPaperIdList = async () => {
}) paperIdList.value = await (await getWithExamPapersList({})).data
.then((res) => { }
examRecordsTable.loading = false setPaperIdList()
examRecordsTable.data = res.data.data
examRecordsTable.total = res.data.total const resetForm = (formEl: FormInstance | undefined) => {
}) if (!formEl) return
.catch(() => { formEl.resetFields()
examRecordsTable.loading = false loadExamRecordsList()
}) }
} </script>
loadExamRecordsList()
<style lang="scss" scoped>
const editExamRecordsDialog: Record<string, any> | null = ref(null) /* 多行超出隐藏 */
.multi-hidden {
/** word-break: break-all;
* 添加考试记录 text-overflow: ellipsis;
*/ overflow: hidden;
const addEvent = () => { display: -webkit-box;
editExamRecordsDialog.value.setFormData() -webkit-line-clamp: 2;
editExamRecordsDialog.value.showDialog = true -webkit-box-orient: vertical;
} }
</style>
/**
* 编辑考试记录
* @param data
*/
const editEvent = (data: any) => {
editExamRecordsDialog.value.setFormData(data)
editExamRecordsDialog.value.showDialog = true
}
/**
* 删除考试记录
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('examRecordsDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteExamRecords(id)
.then(() => {
loadExamRecordsList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadExamRecordsList()
}
</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>

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

@ -1,259 +1,251 @@
<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> </template>
<el-form-item :label="t('remarks')"> <script lang="ts" setup>
<el-input import { ref, reactive, computed, watch } from 'vue'
v-model="formData.remarks" import { useDictionary } from '@/app/api/dict'
clearable import { t } from '@/lang'
:placeholder="t('remarksPlaceholder')" import type { FormInstance } from 'element-plus'
class="input-width" import { addPerformanceRecords, editPerformanceRecords, getPerformanceRecordsInfo, getWithPersonnelList, getWithCustomerResourcesList, getWithOrderTableList } from '@/app/api/performance_records'
/>
</el-form-item> let showDialog = ref(false)
</el-form> const loading = ref(false)
<template #footer> /**
<span class="dialog-footer"> * 表单数据
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button> */
<el-button const initialFormData = {
type="primary" id: '',
:loading="loading" staff_id: '',
@click="confirm(formRef)" resource_id: '',
>{{ t('confirm') }}</el-button order_status: '',
> performance_type: '',
</span> performance_value: '',
</template> remarks: '',
</el-dialog> }
</template> const formData: Record<string, any> = reactive({ ...initialFormData })
<script lang="ts" setup> const formRef = ref<FormInstance>()
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict' //
import { t } from '@/lang' const formRules = computed(() => {
import type { FormInstance } from 'element-plus' return {
import { staff_id: [
addPerformanceRecords, { required: true, message: t('staffIdPlaceholder'), trigger: 'blur' },
editPerformanceRecords,
getPerformanceRecordsInfo, ]
} from '@/app/api/performance_records' ,
resource_id: [
let showDialog = ref(false) { required: true, message: t('resourceIdPlaceholder'), trigger: 'blur' },
const loading = ref(false)
]
/** ,
* 表单数据 order_status: [
*/ { required: true, message: t('orderStatusPlaceholder'), trigger: 'blur' },
const initialFormData = {
id: '', ]
staff_id: '', ,
resource_id: '', performance_type: [
order_id: '', { required: true, message: t('performanceTypePlaceholder'), trigger: 'blur' },
order_status: '',
performance_type: '', ]
performance_value: '', ,
remarks: '', performance_value: [
} { required: true, message: t('performanceValuePlaceholder'), trigger: 'blur' },
const formData: Record<string, any> = reactive({ ...initialFormData })
]
const formRef = ref<FormInstance>() ,
remarks: [
// { required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
const formRules = computed(() => {
return { ]
staff_id: [ ,
{ required: true, message: t('staffIdPlaceholder'), trigger: 'blur' }, }
], })
resource_id: [
{ required: true, message: t('resourceIdPlaceholder'), trigger: 'blur' }, const emit = defineEmits(['complete'])
],
order_id: [ /**
{ required: true, message: t('orderIdPlaceholder'), trigger: 'blur' }, * 确认
], * @param formEl
order_status: [ */
{ required: true, message: t('orderStatusPlaceholder'), trigger: 'blur' }, const confirm = async (formEl: FormInstance | undefined) => {
], if (loading.value || !formEl) return
performance_type: [ let save = formData.id ? editPerformanceRecords : addPerformanceRecords
{
required: true, await formEl.validate(async (valid) => {
message: t('performanceTypePlaceholder'), if (valid) {
trigger: 'blur', loading.value = true
},
], let data = formData
performance_value: [
{ save(data).then(res => {
required: true, loading.value = false
message: t('performanceValuePlaceholder'), showDialog.value = false
trigger: 'blur', emit('complete')
}, }).catch(err => {
], loading.value = false
remarks: [ })
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' }, }
], })
} }
})
//
const emit = defineEmits(['complete']) let order_statusList = ref([])
const order_statusDictList = async () => {
/** order_statusList.value = await (await useDictionary('orderstatus')).data.dictionary
* 确认 }
* @param formEl order_statusDictList();
*/ watch(() => order_statusList.value, () => { formData.order_status = order_statusList.value[0].value })
const confirm = async (formEl: FormInstance | undefined) => { let performance_typeList = ref([])
if (loading.value || !formEl) return const performance_typeDictList = async () => {
let save = formData.id ? editPerformanceRecords : addPerformanceRecords performance_typeList.value = await (await useDictionary('performance_type')).data.dictionary
}
await formEl.validate(async (valid) => { performance_typeDictList();
if (valid) { watch(() => performance_typeList.value, () => { formData.performance_type = performance_typeList.value[0].value })
loading.value = true
let data = formData const staffIdList = ref([] as any[])
const setStaffIdList = async () => {
save(data) staffIdList.value = await (await getWithPersonnelList({})).data
.then((res) => { }
loading.value = false setStaffIdList()
showDialog.value = false const resourceIdList = ref([] as any[])
emit('complete') const setResourceIdList = async () => {
}) resourceIdList.value = await (await getWithCustomerResourcesList({})).data
.catch((err) => { }
loading.value = false setResourceIdList()
}) const orderIdList = ref([] as any[])
} const setOrderIdList = async () => {
}) orderIdList.value = await (await getWithOrderTableList({})).data
} }
setOrderIdList()
// const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
const setFormData = async (row: any = null) => { loading.value = true
Object.assign(formData, initialFormData) if(row){
loading.value = true const data = await (await getPerformanceRecordsInfo(row.id)).data
if (row) { if (data) Object.keys(formData).forEach((key: string) => {
const data = await (await getPerformanceRecordsInfo(row.id)).data if (data[key] != undefined) formData[key] = data[key]
if (data) })
Object.keys(formData).forEach((key: string) => { }
if (data[key] != undefined) formData[key] = data[key] loading.value = false
}) }
}
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')))
const mobileVerify = (rule: any, value: any, callback: any) => { } else {
if (value && !/^1[3-9]\d{9}$/.test(value)) { callback()
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')))
const idCardVerify = (rule: any, value: any, callback: any) => { } else {
if ( callback()
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
) //
) { const emailVerify = (rule: any, value: any, callback: any) => {
callback(new Error(t('generateIdCard'))) if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
} else { callback(new Error(t('generateEmail')))
callback() } else {
} callback()
} }
}
//
const emailVerify = (rule: any, value: any, callback: any) => { //
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { const numberVerify = (rule: any, value: any, callback: any) => {
callback(new Error(t('generateEmail'))) if (!Number.isInteger(value)) {
} else { callback(new Error(t('generateNumber')))
callback() } else {
} callback()
} }
}
//
const numberVerify = (rule: any, value: any, callback: any) => { defineExpose({
if (!Number.isInteger(value)) { showDialog,
callback(new Error(t('generateNumber'))) setFormData
} else { })
callback() </script>
}
} <style lang="scss" scoped></style>
<style lang="scss">
defineExpose({ .diy-dialog-wrap .el-form-item__label{
showDialog, height: auto !important;
setFormData, }
}) </style>
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

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

@ -1,284 +1,236 @@
<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" </template>
:label="t('orderId')"
min-width="120" <script lang="ts" setup>
:show-overflow-tooltip="true" import { reactive, ref, watch } from 'vue'
/> import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
<el-table-column import { getPerformanceRecordsList, deletePerformanceRecords, getWithPersonnelList, getWithCustomerResourcesList, getWithOrderTableList } from '@/app/api/performance_records'
prop="order_status" import { img } from '@/utils/common'
:label="t('orderStatus')" import { ElMessageBox,FormInstance } from 'element-plus'
min-width="120" import Edit from '@/app/views/performance_records/components/performance-records-edit.vue'
:show-overflow-tooltip="true" import { useRoute } from 'vue-router'
/> const route = useRoute()
const pageName = route.meta.title;
<el-table-column
prop="performance_type" let performanceRecordsTable = reactive({
:label="t('performanceType')" page: 1,
min-width="120" limit: 10,
:show-overflow-tooltip="true" total: 0,
/> loading: true,
data: [],
<el-table-column searchParam:{
prop="performance_value" "staff_id":"",
:label="t('performanceValue')" "order_status":"",
min-width="120" "created_at":[]
:show-overflow-tooltip="true" }
/> })
<el-table-column const searchFormRef = ref<FormInstance>()
prop="remarks"
:label="t('remarks')" //
min-width="120" const selectData = ref<any[]>([])
:show-overflow-tooltip="true"
/> //
const order_statusList = ref([] as any[])
<el-table-column const order_statusDictList = async () => {
:label="t('operation')" order_statusList.value = await (await useDictionary('orderstatus')).data.dictionary
fixed="right" }
min-width="120" order_statusDictList();
> const performance_typeList = ref([] as any[])
<template #default="{ row }"> const performance_typeDictList = async () => {
<el-button type="primary" link @click="editEvent(row)">{{ performance_typeList.value = await (await useDictionary('performance_type')).data.dictionary
t('edit') }
}}</el-button> performance_typeDictList();
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete') /**
}}</el-button> * 获取绩效记录列表
</template> */
</el-table-column> const loadPerformanceRecordsList = (page: number = 1) => {
</el-table> performanceRecordsTable.loading = true
<div class="mt-[16px] flex justify-end"> performanceRecordsTable.page = page
<el-pagination
v-model:current-page="performanceRecordsTable.page" getPerformanceRecordsList({
v-model:page-size="performanceRecordsTable.limit" page: performanceRecordsTable.page,
layout="total, sizes, prev, pager, next, jumper" limit: performanceRecordsTable.limit,
:total="performanceRecordsTable.total" ...performanceRecordsTable.searchParam
@size-change="loadPerformanceRecordsList()" }).then(res => {
@current-change="loadPerformanceRecordsList" performanceRecordsTable.loading = false
/> performanceRecordsTable.data = res.data.data
</div> performanceRecordsTable.total = res.data.total
</div> }).catch(() => {
performanceRecordsTable.loading = false
<edit })
ref="editPerformanceRecordsDialog" }
@complete="loadPerformanceRecordsList" loadPerformanceRecordsList()
/>
</el-card> const editPerformanceRecordsDialog: Record<string, any> | null = ref(null)
</div>
</template> /**
* 添加绩效记录
<script lang="ts" setup> */
import { reactive, ref, watch } from 'vue' const addEvent = () => {
import { t } from '@/lang' editPerformanceRecordsDialog.value.setFormData()
import { useDictionary } from '@/app/api/dict' editPerformanceRecordsDialog.value.showDialog = true
import { }
getPerformanceRecordsList,
deletePerformanceRecords, /**
} from '@/app/api/performance_records' * 编辑绩效记录
import { img } from '@/utils/common' * @param data
import { ElMessageBox, FormInstance } from 'element-plus' */
import Edit from '@/app/views/performance_records/components/performance-records-edit.vue' const editEvent = (data: any) => {
import { useRoute } from 'vue-router' editPerformanceRecordsDialog.value.setFormData(data)
const route = useRoute() editPerformanceRecordsDialog.value.showDialog = true
const pageName = route.meta.title }
let performanceRecordsTable = reactive({ /**
page: 1, * 删除绩效记录
limit: 10, */
total: 0, const deleteEvent = (id: number) => {
loading: true, ElMessageBox.confirm(t('performanceRecordsDeleteTips'), t('warning'),
data: [], {
searchParam: { confirmButtonText: t('confirm'),
staff_id: '', cancelButtonText: t('cancel'),
resource_id: '', type: 'warning',
order_id: '', }
order_status: '', ).then(() => {
performance_type: '', deletePerformanceRecords(id).then(() => {
performance_value: '', loadPerformanceRecordsList()
remarks: '', }).catch(() => {
}, })
}) })
}
const searchFormRef = ref<FormInstance>()
// const staffIdList = ref([])
const selectData = ref<any[]>([]) const setStaffIdList = async () => {
staffIdList.value = await (await getWithPersonnelList({})).data
// }
setStaffIdList()
/** const resourceIdList = ref([])
* 获取绩效记录列表 const setResourceIdList = async () => {
*/ resourceIdList.value = await (await getWithCustomerResourcesList({})).data
const loadPerformanceRecordsList = (page: number = 1) => { }
performanceRecordsTable.loading = true setResourceIdList()
performanceRecordsTable.page = page const orderIdList = ref([])
const setOrderIdList = async () => {
getPerformanceRecordsList({ orderIdList.value = await (await getWithOrderTableList({})).data
page: performanceRecordsTable.page, }
limit: performanceRecordsTable.limit, setOrderIdList()
...performanceRecordsTable.searchParam,
}) const resetForm = (formEl: FormInstance | undefined) => {
.then((res) => { if (!formEl) return
performanceRecordsTable.loading = false formEl.resetFields()
performanceRecordsTable.data = res.data.data loadPerformanceRecordsList()
performanceRecordsTable.total = res.data.total }
}) </script>
.catch(() => {
performanceRecordsTable.loading = false <style lang="scss" scoped>
}) /* 多行超出隐藏 */
} .multi-hidden {
loadPerformanceRecordsList() word-break: break-all;
text-overflow: ellipsis;
const editPerformanceRecordsDialog: Record<string, any> | null = ref(null) overflow: hidden;
display: -webkit-box;
/** -webkit-line-clamp: 2;
* 添加绩效记录 -webkit-box-orient: vertical;
*/ }
const addEvent = () => { </style>
editPerformanceRecordsDialog.value.setFormData()
editPerformanceRecordsDialog.value.showDialog = true
}
/**
* 编辑绩效记录
* @param data
*/
const editEvent = (data: any) => {
editPerformanceRecordsDialog.value.setFormData(data)
editPerformanceRecordsDialog.value.showDialog = true
}
/**
* 删除绩效记录
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('performanceRecordsDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deletePerformanceRecords(id)
.then(() => {
loadPerformanceRecordsList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadPerformanceRecordsList()
}
</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>

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());
}
} }

9
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],
@ -87,5 +90,5 @@ class ExamPapers extends BaseAdminController
return success('DELETE_SUCCESS'); return success('DELETE_SUCCESS');
} }
} }

37
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');
@ -114,5 +91,5 @@ class ExamQuestions extends BaseAdminController
return success('DELETE_SUCCESS'); return success('DELETE_SUCCESS');
} }
} }

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

68
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
*/
protected $defaultSoftDelete = 0;
/** /**
* 搜索器:校区校区名称 * 搜索器:考勤校区
* @param $value
* @param $data
*/
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']);
}
} }

136
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
* @param $data
*/
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 $data
*/
public function searchCampusPreviewImageAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_preview_image", $value);
}
}
/**
* 搜索器:校区校区坐标,格式为经度,纬度
* @param $value
* @param $data
*/
public function searchCampusCoordinatesAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_coordinates", $value);
}
}
/**
* 搜索器:校区校区介绍
* @param $value
* @param $data
*/
public function searchCampusIntroductionAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_introduction", $value);
}
}
/** /**
* 搜索器:校区校区状态:0-禁用,1-启用 * 定义软删除字段的默认值.
* @param $value * @var int
* @param $data */
*/ protected $defaultSoftDelete = 0;
public function searchCampusStatusAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_status", $value);
}
}
/** /**
* 搜索器:校区校区创建时间 * 搜索器:合同合同状态
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCreateTimeAttr($query, $value, $data) public function searchContractStatusAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("create_time", $value); $query->where("contract_status", $value);
} }
} }
/** /**
* 搜索器:校区校区更新时间 * 搜索器:合同合同类型
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchUpdateTimeAttr($query, $value, $data) public function searchContractTypeAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("update_time", $value); $query->where("contract_type", $value);
} }
} }
/** /**
* 搜索器:校区逻辑删除字段,NULL表示未删除,非空表示已删除 * 搜索器:合同创建时间
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchDeleteTimeAttr($query, $value, $data) public function searchCreatedAtAttr($query, $value, $data)
{ {
if ($value) { $start = empty($value[0]) ? 0 : strtotime($value[0]);
$query->where("delete_time", $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]]);
} }
} }

78
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);
}
}
/** public function campus(){
* 搜索器:答题记录是否正确 return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']);
* @param $value
* @param $data
*/
public function searchIsCorrectAttr($query, $value, $data)
{
if ($value) {
$query->where("is_correct", $value);
}
} }
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);
} }
} }

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

@ -24,7 +24,7 @@ use think\model\relation\HasOne;
class ExamQuestions extends BaseModel class ExamQuestions extends BaseModel
{ {
/** /**
* 数据表主键 * 数据表主键
@ -38,24 +38,29 @@ class ExamQuestions extends BaseModel
*/ */
protected $name = 'exam_questions'; protected $name = 'exam_questions';
// 设置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
*/ */
@ -65,142 +70,28 @@ class ExamQuestions extends BaseModel
$query->where("question_type", $value); $query->where("question_type", $value);
} }
} }
/**
* 搜索器:试题题干类型: 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 $data
*/
public function searchOptionBContentAttr($query, $value, $data)
{
if ($value) {
$query->where("option_b_content", $value);
}
}
/**
* 搜索器:试题选项C类型: text-文本, image-图片
* @param $value
* @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 $value
* @param $data * @param $data
*/ */
public function searchCorrectAnswerAttr($query, $value, $data) public function searchCreatedAtAttr($query, $value, $data)
{ {
if ($value) { $start = empty($value[0]) ? 0 : strtotime($value[0]);
$query->where("correct_answer", $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]]);
} }
} }
} }

84
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']);
}
} }

96
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,38 +73,20 @@ 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) {
* @param $value $query->where([["created_at", "<=", $end]]);
* @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);
} }
} }
@ -143,4 +95,16 @@ class PerformanceRecords extends BaseModel
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']);
}
} }

111
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 * @var string
*/ */
protected $deleteTime = 'deleted_at'; protected $deleteTime = 'deleted_at';
/** /**
* 定义软删除字段的默认值 * 定义软删除字段的默认值.
* @var int * @var int
*/ */
protected $defaultSoftDelete = 0; protected $defaultSoftDelete = 0;
/**
* 搜索器:学员校区
* @param $value
* @param $data
*/
public function searchCampusIdAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_id", $value);
}
}
/**
* 搜索器:学员学员姓名
* @param $value
* @param $data
*/
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();
}
} }

8
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;
@ -94,6 +94,6 @@ class ExamPapersService extends BaseAdminService
return $res; return $res;
} }
} }

8
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;
@ -94,6 +94,6 @@ class ExamQuestionsService extends BaseAdminService
return $res; return $res;
} }
} }

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