于宏哲PHP 1 year ago
parent
commit
a0d8d36334
  1. 3
      .gitignore
  2. 4
      admin/.env.development
  3. 4
      admin/.env.production
  4. 2
      admin/auto-imports.d.ts
  5. 17
      admin/components.d.ts
  6. 58
      admin/src/addon/zhjw/api/campuses.ts
  7. 58
      admin/src/addon/zhjw/api/classes.ts
  8. 54
      admin/src/addon/zhjw/api/courses.ts
  9. 58
      admin/src/addon/zhjw/api/roles.ts
  10. 56
      admin/src/addon/zhjw/api/staff.ts
  11. 58
      admin/src/addon/zhjw/api/timetables.ts
  12. 58
      admin/src/addon/zhjw/api/venues.ts
  13. 19
      admin/src/addon/zhjw/lang/zh-cn/campuses.campuses.json
  14. 21
      admin/src/addon/zhjw/lang/zh-cn/campuses.campuses_edit.json
  15. 19
      admin/src/addon/zhjw/lang/zh-cn/classes.classes.json
  16. 19
      admin/src/addon/zhjw/lang/zh-cn/classes.classes_edit.json
  17. 22
      admin/src/addon/zhjw/lang/zh-cn/courses.courses.json
  18. 25
      admin/src/addon/zhjw/lang/zh-cn/courses.courses_edit.json
  19. 10
      admin/src/addon/zhjw/lang/zh-cn/roles.roles.json
  20. 11
      admin/src/addon/zhjw/lang/zh-cn/roles.roles_edit.json
  21. 21
      admin/src/addon/zhjw/lang/zh-cn/staff.staff.json
  22. 21
      admin/src/addon/zhjw/lang/zh-cn/staff.staff_edit.json
  23. 11
      admin/src/addon/zhjw/lang/zh-cn/timetables.timetables.json
  24. 11
      admin/src/addon/zhjw/lang/zh-cn/timetables.timetables_edit.json
  25. 19
      admin/src/addon/zhjw/lang/zh-cn/venues.venues.json
  26. 19
      admin/src/addon/zhjw/lang/zh-cn/venues.venues_edit.json
  27. 207
      admin/src/addon/zhjw/views/campuses/campuses.vue
  28. 221
      admin/src/addon/zhjw/views/campuses/campuses_edit.vue
  29. 225
      admin/src/addon/zhjw/views/classes/classes.vue
  30. 236
      admin/src/addon/zhjw/views/classes/classes_edit.vue
  31. 249
      admin/src/addon/zhjw/views/classes/components/classes-edit.vue
  32. 255
      admin/src/addon/zhjw/views/courses/components/courses-edit.vue
  33. 233
      admin/src/addon/zhjw/views/courses/courses.vue
  34. 252
      admin/src/addon/zhjw/views/courses/courses_edit.vue
  35. 179
      admin/src/addon/zhjw/views/roles/roles.vue
  36. 171
      admin/src/addon/zhjw/views/roles/roles_edit.vue
  37. 248
      admin/src/addon/zhjw/views/staff/staff.vue
  38. 246
      admin/src/addon/zhjw/views/staff/staff_edit.vue
  39. 187
      admin/src/addon/zhjw/views/timetables/components/timetables-edit.vue
  40. 197
      admin/src/addon/zhjw/views/timetables/timetables.vue
  41. 184
      admin/src/addon/zhjw/views/timetables/timetables_edit.vue
  42. 242
      admin/src/addon/zhjw/views/venues/venues.vue
  43. 236
      admin/src/addon/zhjw/views/venues/venues_edit.vue
  44. 2
      niucloud/.env
  45. 58
      niucloud/addon/zhjw/admin/api/campuses.ts
  46. 58
      niucloud/addon/zhjw/admin/api/classes.ts
  47. 54
      niucloud/addon/zhjw/admin/api/courses.ts
  48. 58
      niucloud/addon/zhjw/admin/api/roles.ts
  49. 56
      niucloud/addon/zhjw/admin/api/staff.ts
  50. 58
      niucloud/addon/zhjw/admin/api/timetables.ts
  51. 58
      niucloud/addon/zhjw/admin/api/venues.ts
  52. 19
      niucloud/addon/zhjw/admin/lang/zh-cn/campuses.campuses.json
  53. 21
      niucloud/addon/zhjw/admin/lang/zh-cn/campuses.campuses_edit.json
  54. 19
      niucloud/addon/zhjw/admin/lang/zh-cn/classes.classes.json
  55. 19
      niucloud/addon/zhjw/admin/lang/zh-cn/classes.classes_edit.json
  56. 22
      niucloud/addon/zhjw/admin/lang/zh-cn/courses.courses.json
  57. 25
      niucloud/addon/zhjw/admin/lang/zh-cn/courses.courses_edit.json
  58. 10
      niucloud/addon/zhjw/admin/lang/zh-cn/roles.roles.json
  59. 11
      niucloud/addon/zhjw/admin/lang/zh-cn/roles.roles_edit.json
  60. 21
      niucloud/addon/zhjw/admin/lang/zh-cn/staff.staff.json
  61. 21
      niucloud/addon/zhjw/admin/lang/zh-cn/staff.staff_edit.json
  62. 11
      niucloud/addon/zhjw/admin/lang/zh-cn/timetables.timetables.json
  63. 11
      niucloud/addon/zhjw/admin/lang/zh-cn/timetables.timetables_edit.json
  64. 19
      niucloud/addon/zhjw/admin/lang/zh-cn/venues.venues.json
  65. 19
      niucloud/addon/zhjw/admin/lang/zh-cn/venues.venues_edit.json
  66. 207
      niucloud/addon/zhjw/admin/views/campuses/campuses.vue
  67. 221
      niucloud/addon/zhjw/admin/views/campuses/campuses_edit.vue
  68. 225
      niucloud/addon/zhjw/admin/views/classes/classes.vue
  69. 236
      niucloud/addon/zhjw/admin/views/classes/classes_edit.vue
  70. 249
      niucloud/addon/zhjw/admin/views/classes/components/classes-edit.vue
  71. 255
      niucloud/addon/zhjw/admin/views/courses/components/courses-edit.vue
  72. 233
      niucloud/addon/zhjw/admin/views/courses/courses.vue
  73. 252
      niucloud/addon/zhjw/admin/views/courses/courses_edit.vue
  74. 179
      niucloud/addon/zhjw/admin/views/roles/roles.vue
  75. 171
      niucloud/addon/zhjw/admin/views/roles/roles_edit.vue
  76. 248
      niucloud/addon/zhjw/admin/views/staff/staff.vue
  77. 246
      niucloud/addon/zhjw/admin/views/staff/staff_edit.vue
  78. 187
      niucloud/addon/zhjw/admin/views/timetables/components/timetables-edit.vue
  79. 197
      niucloud/addon/zhjw/admin/views/timetables/timetables.vue
  80. 184
      niucloud/addon/zhjw/admin/views/timetables/timetables_edit.vue
  81. 242
      niucloud/addon/zhjw/admin/views/venues/venues.vue
  82. 236
      niucloud/addon/zhjw/admin/views/venues/venues_edit.vue
  83. 104
      niucloud/addon/zhjw/app/adminapi/controller/campuses/Campuses.php
  84. 104
      niucloud/addon/zhjw/app/adminapi/controller/classes/Classes.php
  85. 107
      niucloud/addon/zhjw/app/adminapi/controller/courses/Courses.php
  86. 87
      niucloud/addon/zhjw/app/adminapi/controller/roles/Roles.php
  87. 107
      niucloud/addon/zhjw/app/adminapi/controller/staff/Staff.php
  88. 92
      niucloud/addon/zhjw/app/adminapi/controller/timetables/Timetables.php
  89. 104
      niucloud/addon/zhjw/app/adminapi/controller/venues/Venues.php
  90. 177
      niucloud/addon/zhjw/app/adminapi/route/route.php
  91. 130
      niucloud/addon/zhjw/app/model/campuses/Campuses.php
  92. 136
      niucloud/addon/zhjw/app/model/classes/Classes.php
  93. 142
      niucloud/addon/zhjw/app/model/courses/Courses.php
  94. 70
      niucloud/addon/zhjw/app/model/roles/Roles.php
  95. 148
      niucloud/addon/zhjw/app/model/staff/Staff.php
  96. 88
      niucloud/addon/zhjw/app/model/timetables/Timetables.php
  97. 136
      niucloud/addon/zhjw/app/model/venues/Venues.php
  98. 100
      niucloud/addon/zhjw/app/service/admin/campuses/CampusesService.php
  99. 106
      niucloud/addon/zhjw/app/service/admin/classes/ClassesService.php
  100. 101
      niucloud/addon/zhjw/app/service/admin/courses/CoursesService.php

3
.gitignore

@ -21,3 +21,6 @@ bin-release/
niucloud/public/.htaccess niucloud/public/.htaccess
#uni-app/* #uni-app/*
#web/* #web/*
/niucloud/public/admin
/niucloud/runtime
/niucloud/runtime

4
admin/.env.development

@ -1,8 +1,8 @@
# api请求地址 # api请求地址
VITE_APP_BASE_URL='http://146.56.228.75:20021/adminapi/' VITE_APP_BASE_URL='http://zhjw.cc/adminapi/'
# 图片服务器地址 # 图片服务器地址
VITE_IMG_DOMAIN='' VITE_IMG_DOMAIN='http://zhjw.cc'
# 请求时header中token的参数名 # 请求时header中token的参数名
VITE_REQUEST_HEADER_TOKEN_KEY='token' VITE_REQUEST_HEADER_TOKEN_KEY='token'

4
admin/.env.production

@ -1,8 +1,8 @@
# api请求地址 # api请求地址
VITE_APP_BASE_URL='http://146.56.228.75:20021/adminapi/' VITE_APP_BASE_URL='http://zhjw.cc/adminapi/'
# 图片服务器地址 # 图片服务器地址
VITE_IMG_DOMAIN='http://146.56.228.75:20021' VITE_IMG_DOMAIN='http://zhjw.cc'
# 请求时header中token的参数名 # 请求时header中token的参数名
VITE_REQUEST_HEADER_TOKEN_KEY='token' VITE_REQUEST_HEADER_TOKEN_KEY='token'

2
admin/auto-imports.d.ts

@ -1,5 +1,5 @@
// Generated by 'unplugin-auto-import' // Generated by 'unplugin-auto-import'
export {} export {}
declare global { declare global {
const ElNotification: typeof import('element-plus/es')['ElNotification']
} }

17
admin/components.d.ts

@ -17,11 +17,20 @@ declare module '@vue/runtime-core' {
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard'] ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElCascader: typeof import('element-plus/es')['ElCascader']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol'] ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer'] ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown'] ElDropdown: typeof import('element-plus/es')['ElDropdown']
@ -41,15 +50,20 @@ declare module '@vue/runtime-core' {
ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup']
ElPageHeader: typeof import('element-plus/es')['ElPageHeader'] ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
ElPagination: typeof import('element-plus/es')['ElPagination'] ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover'] ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRate: typeof import('element-plus/es')['ElRate']
ElResult: typeof import('element-plus/es')['ElResult'] ElResult: typeof import('element-plus/es')['ElResult']
ElRow: typeof import('element-plus/es')['ElRow'] ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElStatistic: typeof import('element-plus/es')['ElStatistic'] ElStatistic: typeof import('element-plus/es')['ElStatistic']
ElStep: typeof import('element-plus/es')['ElStep'] ElStep: typeof import('element-plus/es')['ElStep']
ElSteps: typeof import('element-plus/es')['ElSteps'] ElSteps: typeof import('element-plus/es')['ElSteps']
@ -60,6 +74,9 @@ declare module '@vue/runtime-core' {
ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs'] ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag'] ElTag: typeof import('element-plus/es')['ElTag']
ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree'] ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect'] ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']

58
admin/src/addon/zhjw/api/campuses.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- campuses
/**
*
* @param params
* @returns
*/
export function getCampusesList(params: Record<string, any>) {
return request.get(`zhjw/campuses`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getCampusesInfo(id: number) {
return request.get(`zhjw/campuses/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addCampuses(params: Record<string, any>) {
return request.post('zhjw/campuses', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editCampuses(params: Record<string, any>) {
return request.put(`zhjw/campuses/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteCampuses(id: number) {
return request.delete(`zhjw/campuses/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
// USER_CODE_END -- campuses

58
admin/src/addon/zhjw/api/classes.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- classes
/**
*
* @param params
* @returns
*/
export function getClassesList(params: Record<string, any>) {
return request.get(`zhjw/classes`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getClassesInfo(id: number) {
return request.get(`zhjw/classes/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addClasses(params: Record<string, any>) {
return request.post('zhjw/classes', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editClasses(params: Record<string, any>) {
return request.put(`zhjw/classes/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteClasses(id: number) {
return request.delete(`zhjw/classes/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithVenuesList(params: Record<string,any>){
return request.get('zhjw/venues_all', {params})
}
// USER_CODE_END -- classes

54
admin/src/addon/zhjw/api/courses.ts

@ -0,0 +1,54 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- courses
/**
*
* @param params
* @returns
*/
export function getCoursesList(params: Record<string, any>) {
return request.get(`zhjw/courses`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getCoursesInfo(id: number) {
return request.get(`zhjw/courses/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addCourses(params: Record<string, any>) {
return request.post('zhjw/courses', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editCourses(params: Record<string, any>) {
return request.put(`zhjw/courses/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteCourses(id: number) {
return request.delete(`zhjw/courses/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
// USER_CODE_END -- courses

58
admin/src/addon/zhjw/api/roles.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- roles
/**
*
* @param params
* @returns
*/
export function getRolesList(params: Record<string, any>) {
return request.get(`zhjw/roles`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getRolesInfo(id: number) {
return request.get(`zhjw/roles/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addRoles(params: Record<string, any>) {
return request.post('zhjw/roles', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editRoles(params: Record<string, any>) {
return request.put(`zhjw/roles/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteRoles(id: number) {
return request.delete(`zhjw/roles/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
// USER_CODE_END -- roles

56
admin/src/addon/zhjw/api/staff.ts

@ -0,0 +1,56 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- staff
/**
*
* @param params
* @returns
*/
export function getStaffList(params: Record<string, any>) {
return request.get(`zhjw/staff`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getStaffInfo(id: number) {
return request.get(`zhjw/staff/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addStaff(params: Record<string, any>) {
return request.post('zhjw/staff', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editStaff(params: Record<string, any>) {
return request.put(`zhjw/staff/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteStaff(id: number) {
return request.delete(`zhjw/staff/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithRolesList(params: Record<string,any>){
return request.get('zhjw/roles_all', {params})
}
// USER_CODE_END -- staff

58
admin/src/addon/zhjw/api/timetables.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- timetables
/**
*
* @param params
* @returns
*/
export function getTimetablesList(params: Record<string, any>) {
return request.get(`zhjw/timetables`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getTimetablesInfo(id: number) {
return request.get(`zhjw/timetables/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addTimetables(params: Record<string, any>) {
return request.post('zhjw/timetables', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editTimetables(params: Record<string, any>) {
return request.put(`zhjw/timetables/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteTimetables(id: number) {
return request.delete(`zhjw/timetables/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithClassesList(params: Record<string,any>){
return request.get('zhjw/classes_all', {params})
}
// USER_CODE_END -- timetables

58
admin/src/addon/zhjw/api/venues.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- venues
/**
*
* @param params
* @returns
*/
export function getVenuesList(params: Record<string, any>) {
return request.get(`zhjw/venues`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getVenuesInfo(id: number) {
return request.get(`zhjw/venues/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addVenues(params: Record<string, any>) {
return request.post('zhjw/venues', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editVenues(params: Record<string, any>) {
return request.put(`zhjw/venues/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteVenues(id: number) {
return request.delete(`zhjw/venues/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithCampusesList(params: Record<string,any>){
return request.get('zhjw/campuses_all', {params})
}
// USER_CODE_END -- venues

19
admin/src/addon/zhjw/lang/zh-cn/campuses.campuses.json

@ -0,0 +1,19 @@
{
"name":"校区名称",
"namePlaceholder":"请输入校区名称",
"coordinate":"坐标地址",
"coordinatePlaceholder":"请输入坐标地址",
"address":"校区地址",
"addressPlaceholder":"请输入校区地址",
"contactPerson":"联系人",
"contactPersonPlaceholder":"请输入联系人",
"contactPhone":"联系电话",
"contactPhonePlaceholder":"请输入联系电话",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addCampuses":"添加校区管理",
"updateCampuses":"编辑校区管理",
"campusesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

21
admin/src/addon/zhjw/lang/zh-cn/campuses.campuses_edit.json

@ -0,0 +1,21 @@
{
"name":"校区名称",
"coordinate":"坐标地址",
"address":"校区地址",
"contactPerson":"联系人",
"contactPhone":"联系电话",
"status":"状态",
"thumbnail":"缩略图",
"description":"校区描述",
"namePlaceholder":"请输入校区名称",
"coordinatePlaceholder":"请输入坐标地址",
"addressPlaceholder":"请输入校区地址",
"contactPersonPlaceholder":"请输入联系人",
"contactPhonePlaceholder":"请输入联系电话",
"statusPlaceholder":"请输入状态",
"thumbnailPlaceholder":"请上传缩略图",
"descriptionPlaceholder":"请输入校区描述",
"addCampuses":"添加校区管理",
"updateCampuses":"编辑校区管理",
"campusesDeleteTips":"确定要删除该校区管理吗?"
}

19
admin/src/addon/zhjw/lang/zh-cn/classes.classes.json

@ -0,0 +1,19 @@
{
"venueId":"所属场地",
"venueIdPlaceholder":"全部",
"name":"班级名称",
"namePlaceholder":"请输入班级名称",
"maxStudents":"最大学员数",
"maxStudentsPlaceholder":"请输入最大学员数",
"startDate":"开班时间",
"startDatePlaceholder":"请输入开班时间",
"endDate":"结班时间",
"endDatePlaceholder":"请输入结班时间",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addClasses":"添加班级管理",
"updateClasses":"编辑班级管理",
"classesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

19
admin/src/addon/zhjw/lang/zh-cn/classes.classes_edit.json

@ -0,0 +1,19 @@
{
"venueId":"所属场地",
"thumbnail":"缩略图",
"name":"班级名称",
"maxStudents":"最大学员数",
"startDate":"开班时间",
"endDate":"结班时间",
"status":"状态",
"venueIdPlaceholder":"请输入所属场地",
"thumbnailPlaceholder":"请上传缩略图",
"namePlaceholder":"请输入班级名称",
"maxStudentsPlaceholder":"请输入最大学员数",
"startDatePlaceholder":"请选择开班时间",
"endDatePlaceholder":"请选择结班时间",
"statusPlaceholder":"请输入状态",
"addClasses":"添加班级管理",
"updateClasses":"编辑班级管理",
"classesDeleteTips":"确定要删除该班级管理吗?"
}

22
admin/src/addon/zhjw/lang/zh-cn/courses.courses.json

@ -0,0 +1,22 @@
{
"name":"课程名称",
"namePlaceholder":"请输入课程名称",
"description":"课程描述",
"descriptionPlaceholder":"请输入课程描述",
"targetGroup":"适合人群",
"targetGroupPlaceholder":"请输入适合人群",
"duration":"课时数",
"tastePrice":"体验价格",
"tastePricePlaceholder":"请输入体验价格",
"price":"正式价格",
"pricePlaceholder":"请输入正式价格",
"isAutomaticSigning":"是否自动签约",
"isAutomaticSigningPlaceholder":"请输入是否自动签约",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addCourses":"添加课程管理",
"updateCourses":"编辑课程管理",
"coursesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

25
admin/src/addon/zhjw/lang/zh-cn/courses.courses_edit.json

@ -0,0 +1,25 @@
{
"name":"课程名称",
"description":"课程描述",
"thumbnail":"缩略图",
"targetGroup":"适合人群",
"duration":"课时数",
"tastePrice":"体验价格",
"price":"正式价格",
"isAutomaticSigning":"是否自动签约",
"automaticSigningTime":"自动签约",
"status":"状态",
"namePlaceholder":"请输入课程名称",
"descriptionPlaceholder":"请输入课程描述",
"thumbnailPlaceholder":"请上传缩略图",
"targetGroupPlaceholder":"请输入适合人群",
"durationPlaceholder":"请输入课时数",
"tastePricePlaceholder":"请输入体验价格",
"pricePlaceholder":"请输入正式价格",
"isAutomaticSigningPlaceholder":"请输入是否自动签约",
"automaticSigningTimePlaceholder":"请输入自动签约",
"statusPlaceholder":"请输入状态",
"addCourses":"添加课程管理",
"updateCourses":"编辑课程管理",
"coursesDeleteTips":"确定要删除该课程管理吗?"
}

10
admin/src/addon/zhjw/lang/zh-cn/roles.roles.json

@ -0,0 +1,10 @@
{
"id":"ID",
"name":"角色名称",
"namePlaceholder":"请输入角色名称",
"addRoles":"添加角色管理",
"updateRoles":"编辑角色管理",
"rolesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

11
admin/src/addon/zhjw/lang/zh-cn/roles.roles_edit.json

@ -0,0 +1,11 @@
{
"name":"角色名称",
"permissions":"数据权限配置",
"description":"角色描述",
"namePlaceholder":"请输入角色名称",
"permissionsPlaceholder":"请输入数据权限配置",
"descriptionPlaceholder":"请输入角色描述",
"addRoles":"添加角色管理",
"updateRoles":"编辑角色管理",
"rolesDeleteTips":"确定要删除该角色管理吗?"
}

21
admin/src/addon/zhjw/lang/zh-cn/staff.staff.json

@ -0,0 +1,21 @@
{
"name":"姓名",
"namePlaceholder":"请输入姓名",
"gender":"性别",
"genderPlaceholder":"请输入性别",
"phone":"联系方式",
"phonePlaceholder":"请输入联系方式",
"email":"邮箱",
"emailPlaceholder":"请输入邮箱",
"position":"职位",
"positionPlaceholder":"请输入职位",
"status":"状态",
"statusPlaceholder":"请输入状态",
"roleId":"角色关系",
"roleIdPlaceholder":"全部",
"addStaff":"添加人员管理",
"updateStaff":"编辑人员管理",
"staffDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

21
admin/src/addon/zhjw/lang/zh-cn/staff.staff_edit.json

@ -0,0 +1,21 @@
{
"name":"姓名",
"header":"头像",
"gender":"性别",
"phone":"联系方式",
"email":"邮箱",
"position":"职位",
"status":"状态",
"roleId":"角色关系",
"namePlaceholder":"请输入姓名",
"headerPlaceholder":"请上传头像",
"genderPlaceholder":"请输入性别",
"phonePlaceholder":"请输入联系方式",
"emailPlaceholder":"请输入邮箱",
"positionPlaceholder":"请输入职位",
"statusPlaceholder":"请输入状态",
"roleIdPlaceholder":"请输入角色关系",
"addStaff":"添加人员管理",
"updateStaff":"编辑人员管理",
"staffDeleteTips":"确定要删除该人员管理吗?"
}

11
admin/src/addon/zhjw/lang/zh-cn/timetables.timetables.json

@ -0,0 +1,11 @@
{
"classId":"所属班级",
"classIdPlaceholder":"全部",
"cycle":"周期",
"cyclePlaceholder":"请输入周期",
"addTimetables":"添加课表管理",
"updateTimetables":"编辑课表管理",
"timetablesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

11
admin/src/addon/zhjw/lang/zh-cn/timetables.timetables_edit.json

@ -0,0 +1,11 @@
{
"classId":"所属班级",
"scheduleJson":"排课详情",
"cycle":"周期",
"classIdPlaceholder":"请输入所属班级",
"scheduleJsonPlaceholder":"请输入排课详情",
"cyclePlaceholder":"请输入周期",
"addTimetables":"添加课表管理",
"updateTimetables":"编辑课表管理",
"timetablesDeleteTips":"确定要删除该课表管理吗?"
}

19
admin/src/addon/zhjw/lang/zh-cn/venues.venues.json

@ -0,0 +1,19 @@
{
"campusId":"所属校区",
"campusIdPlaceholder":"全部",
"name":"场地名称",
"namePlaceholder":"请输入场地名称",
"type":"场地类型",
"typePlaceholder":"请输入场地类型",
"capacity":"容纳人数",
"capacityPlaceholder":"请输入容纳人数",
"availableTime":"可用时间段",
"availableTimePlaceholder":"请输入可用时间段",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addVenues":"添加场地管理",
"updateVenues":"编辑场地管理",
"venuesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

19
admin/src/addon/zhjw/lang/zh-cn/venues.venues_edit.json

@ -0,0 +1,19 @@
{
"campusId":"所属校区",
"name":"场地名称",
"thumbnail":"缩略图",
"type":"场地类型",
"capacity":"容纳人数",
"availableTime":"可用时间段",
"status":"状态",
"campusIdPlaceholder":"请选择所属校区",
"namePlaceholder":"请输入场地名称",
"thumbnailPlaceholder":"请上传缩略图",
"typePlaceholder":"请输入场地类型",
"capacityPlaceholder":"请输入容纳人数",
"availableTimePlaceholder":"请输入可用时间段",
"statusPlaceholder":"请输入状态",
"addVenues":"添加场地管理",
"updateVenues":"编辑场地管理",
"venuesDeleteTips":"确定要删除该场地管理吗?"
}

207
admin/src/addon/zhjw/views/campuses/campuses.vue

@ -0,0 +1,207 @@
<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('addCampuses') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="campusesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="name">
<el-input v-model="campusesTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('coordinate')" prop="coordinate">
<el-input v-model="campusesTable.searchParam.coordinate" :placeholder="t('coordinatePlaceholder')" />
</el-form-item>
<el-form-item :label="t('address')" prop="address">
<el-input v-model="campusesTable.searchParam.address" :placeholder="t('addressPlaceholder')" />
</el-form-item>
<el-form-item :label="t('contactPerson')" prop="contact_person">
<el-input v-model="campusesTable.searchParam.contact_person" :placeholder="t('contactPersonPlaceholder')" />
</el-form-item>
<el-form-item :label="t('contactPhone')" prop="contact_phone">
<el-input v-model="campusesTable.searchParam.contact_phone" :placeholder="t('contactPhonePlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="campusesTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadCampusesList()">{{ 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="campusesTable.data" size="large" v-loading="campusesTable.loading">
<template #empty>
<span>{{ !campusesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="coordinate" :label="t('coordinate')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="address" :label="t('address')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="contact_person" :label="t('contactPerson')" 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 :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="campusesTable.page" v-model:page-size="campusesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="campusesTable.total"
@size-change="loadCampusesList()" @current-change="loadCampusesList" />
</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 { getCampusesList, deleteCampuses } from '@/addon/zhjw/api/campuses'
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 campusesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"name":"",
"coordinate":"",
"address":"",
"contact_person":"",
"contact_phone":"",
"status":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('is_radio')).data.dictionary
}
statusDictList();
/**
* 获取校区管理列表
*/
const loadCampusesList = (page: number = 1) => {
campusesTable.loading = true
campusesTable.page = page
getCampusesList({
page: campusesTable.page,
limit: campusesTable.limit,
...campusesTable.searchParam
}).then(res => {
campusesTable.loading = false
campusesTable.data = res.data.data
campusesTable.total = res.data.total
}).catch(() => {
campusesTable.loading = false
})
}
loadCampusesList()
const router = useRouter()
/**
* 添加校区管理
*/
const addEvent = () => {
router.push('/campuses/campuses_edit')
}
/**
* 编辑校区管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/campuses/campuses_edit?id='+data.id)
}
/**
* 删除校区管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('campusesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteCampuses(id).then(() => {
loadCampusesList()
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadCampusesList()
}
</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>

221
admin/src/addon/zhjw/views/campuses/campuses_edit.vue

@ -0,0 +1,221 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<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('coordinate')" prop="coordinate">
<el-input v-model="formData.coordinate" clearable :placeholder="t('coordinatePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('address')" prop="address">
<el-input v-model="formData.address" clearable :placeholder="t('addressPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('contactPerson')" prop="contact_person">
<el-input v-model="formData.contact_person" clearable :placeholder="t('contactPersonPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('contactPhone')" prop="contact_phone">
<el-input v-model="formData.contact_phone" clearable :placeholder="t('contactPhonePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('description')" prop="description">
<el-input v-model="formData.description" clearable :placeholder="t('descriptionPlaceholder')" class="input-width" />
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getCampusesInfo,addCampuses,editCampuses } from '@/addon/zhjw/api/campuses';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
name: '',
coordinate: '',
address: '',
contact_person: '',
contact_phone: '',
status: '',
thumbnail: '',
description: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getCampusesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('is_radio')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
coordinate: [
{ required: true, message: t('coordinatePlaceholder'), trigger: 'blur' },
]
,
address: [
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' },
]
,
contact_person: [
{ required: true, message: t('contactPersonPlaceholder'), trigger: 'blur' },
]
,
contact_phone: [
{ required: true, message: t('contactPhonePlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
description: [
{ required: true, message: t('descriptionPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editCampuses : addCampuses
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

225
admin/src/addon/zhjw/views/classes/classes.vue

@ -0,0 +1,225 @@
<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('addClasses') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="classesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('venueId')" prop="venue_id">
<el-select class="w-[280px]" v-model="classesTable.searchParam.venue_id" clearable :placeholder="t('venueIdPlaceholder')">
<el-option
v-for="(item, index) in venueIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('name')" prop="name">
<el-input v-model="classesTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('maxStudents')" prop="max_students">
<el-input v-model="classesTable.searchParam.max_students" :placeholder="t('maxStudentsPlaceholder')" />
</el-form-item>
<el-form-item :label="t('startDate')" prop="start_date">
<el-date-picker v-model="classesTable.searchParam.start_date" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item :label="t('endDate')" prop="end_date">
<el-date-picker v-model="classesTable.searchParam.end_date" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="classesTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadClassesList()">{{ 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="classesTable.data" size="large" v-loading="classesTable.loading">
<template #empty>
<span>{{ !classesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="venue_id_name" :label="t('venueId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="max_students" :label="t('maxStudents')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="start_date" :label="t('startDate')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="end_date" :label="t('endDate')" 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="classesTable.page" v-model:page-size="classesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="classesTable.total"
@size-change="loadClassesList()" @current-change="loadClassesList" />
</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 { getClassesList, deleteClasses, getWithVenuesList } from '@/addon/zhjw/api/classes'
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 classesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"venue_id":"",
"name":"",
"max_students":"",
"start_date":"",
"end_date":"",
"status":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('bj_status')).data.dictionary
}
statusDictList();
/**
* 获取班级管理列表
*/
const loadClassesList = (page: number = 1) => {
classesTable.loading = true
classesTable.page = page
getClassesList({
page: classesTable.page,
limit: classesTable.limit,
...classesTable.searchParam
}).then(res => {
classesTable.loading = false
classesTable.data = res.data.data
classesTable.total = res.data.total
}).catch(() => {
classesTable.loading = false
})
}
loadClassesList()
const router = useRouter()
/**
* 添加班级管理
*/
const addEvent = () => {
router.push('/classes/classes_edit')
}
/**
* 编辑班级管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/classes/classes_edit?id='+data.id)
}
/**
* 删除班级管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('classesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteClasses(id).then(() => {
loadClassesList()
}).catch(() => {
})
})
}
const venueIdList = ref([])
const setVenueIdList = async () => {
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadClassesList()
}
</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>

236
admin/src/addon/zhjw/views/classes/classes_edit.vue

@ -0,0 +1,236 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('venueId')" prop="venue_id">
<el-radio-group v-model="formData.venue_id" :placeholder="t('venueIdPlaceholder')">
<el-radio
v-for="(item, index) in venueIdList"
:key="index"
:label="item['id']">
{{ item['name'] }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</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('maxStudents')" prop="max_students">
<el-input v-model="formData.max_students" clearable :placeholder="t('maxStudentsPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('startDate')" prop="start_date" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.start_date"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('startDatePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('endDate')" prop="end_date" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.end_date"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('endDatePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getClassesInfo,addClasses,editClasses, getWithVenuesList } from '@/addon/zhjw/api/classes';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
venue_id: '',
thumbnail: '',
name: '',
max_students: 0,
start_date: '',
end_date: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getClassesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('bj_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const venueIdList = ref([] as any[])
const setVenueIdList = async () => {
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
//
const formRules = computed(() => {
return {
venue_id: [
{ required: true, message: t('venueIdPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
max_students: [
{ required: true, message: t('maxStudentsPlaceholder'), trigger: 'blur' },
]
,
start_date: [
{ required: true, message: t('startDatePlaceholder'), trigger: 'blur' },
]
,
end_date: [
{ required: true, message: t('endDatePlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editClasses : addClasses
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

249
admin/src/addon/zhjw/views/classes/components/classes-edit.vue

@ -0,0 +1,249 @@
<template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updateClasses') : t('addClasses')" 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('venueId')" prop="venue_id">
<el-select class="input-width" v-model="formData.venue_id" clearable :placeholder="t('venueIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in venueIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('timetableId')" prop="timetable_id">
<el-input v-model="formData.timetable_id" clearable :placeholder="t('timetableIdPlaceholder')" class="input-width" />
</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('maxStudents')" prop="max_students">
<el-input v-model="formData.max_students" clearable :placeholder="t('maxStudentsPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('startDate')" prop="start_date" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.start_date"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('startDatePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('endDate')" prop="end_date" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.end_date"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('endDatePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<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 { addClasses, editClasses, getClassesInfo, getWithVenuesList } from '@/addon/zhjw/api/classes'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
venue_id: '',
thumbnail: '',
timetable_id: '',
name: '',
max_students: '',
start_date: '',
end_date: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
venue_id: [
{ required: true, message: t('venueIdPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
timetable_id: [
{ required: true, message: t('timetableIdPlaceholder'), trigger: 'blur' },
]
,
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
max_students: [
{ required: true, message: t('maxStudentsPlaceholder'), trigger: 'blur' },
]
,
start_date: [
{ required: true, message: t('startDatePlaceholder'), trigger: 'blur' },
]
,
end_date: [
{ required: true, message: t('endDatePlaceholder'), 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 ? editClasses : addClasses
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 statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('bj_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const venueIdList = ref([] as any[])
const setVenueIdList = async () => {
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if(row){
const data = await (await getClassesInfo(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>

255
admin/src/addon/zhjw/views/courses/components/courses-edit.vue

@ -0,0 +1,255 @@
<template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updateCourses') : t('addCourses')" 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('name')" prop="name">
<el-input v-model="formData.name" clearable :placeholder="t('namePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('description')" prop="description">
<editor v-model="formData.description" />
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('targetGroup')" prop="target_group">
<el-input v-model="formData.target_group" clearable :placeholder="t('targetGroupPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('duration')" prop="duration">
<el-input v-model="formData.duration" clearable :placeholder="t('durationPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('tastePrice')" prop="taste_price">
<el-input v-model="formData.taste_price" clearable :placeholder="t('tastePricePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('price')" prop="price">
<el-input v-model="formData.price" clearable :placeholder="t('pricePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('isAutomaticSigning')" prop="is_automatic_signing">
<el-radio-group v-model="formData.is_automatic_signing" :placeholder="t('isAutomaticSigningPlaceholder')">
<el-radio
v-for="(item, index) in is_automatic_signingList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('automaticSigningTime')" prop="automatic_signing_time">
<el-input v-model="formData.automatic_signing_time" clearable :placeholder="t('automaticSigningTimePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<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 { addCourses, editCourses, getCoursesInfo } from '@/addon/zhjw/api/courses'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
name: '',
description: '',
thumbnail: '',
target_group: '',
duration: '',
taste_price: '',
price: '',
is_automatic_signing: '',
automatic_signing_time: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
description: [
{ required: true, message: t('descriptionPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
target_group: [
{ required: true, message: t('targetGroupPlaceholder'), trigger: 'blur' },
]
,
duration: [
{ required: true, message: t('durationPlaceholder'), trigger: 'blur' },
]
,
taste_price: [
{ required: true, message: t('tastePricePlaceholder'), trigger: 'blur' },
]
,
price: [
{ required: true, message: t('pricePlaceholder'), trigger: 'blur' },
]
,
is_automatic_signing: [
{ required: true, message: t('isAutomaticSigningPlaceholder'), trigger: 'blur' },
]
,
automatic_signing_time: [
{ required: true, message: t('automaticSigningTimePlaceholder'), 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 ? editCourses : addCourses
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 is_automatic_signingList = ref([])
const is_automatic_signingDictList = async () => {
is_automatic_signingList.value = await (await useDictionary('is_radio')).data.dictionary
}
is_automatic_signingDictList();
watch(() => is_automatic_signingList.value, () => { formData.is_automatic_signing = is_automatic_signingList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('config_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if(row){
const data = await (await getCoursesInfo(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>

233
admin/src/addon/zhjw/views/courses/courses.vue

@ -0,0 +1,233 @@
<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('addCourses') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="coursesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="name">
<el-input v-model="coursesTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('targetGroup')" prop="target_group">
<el-input v-model="coursesTable.searchParam.target_group" :placeholder="t('targetGroupPlaceholder')" />
</el-form-item>
<el-form-item :label="t('tastePrice')" prop="taste_price">
<el-input v-model="coursesTable.searchParam.taste_price" :placeholder="t('tastePricePlaceholder')" />
</el-form-item>
<el-form-item :label="t('price')" prop="price">
<el-input v-model="coursesTable.searchParam.price" :placeholder="t('pricePlaceholder')" />
</el-form-item>
<el-form-item :label="t('isAutomaticSigning')" prop="is_automatic_signing">
<el-select class="w-[280px]" v-model="coursesTable.searchParam.is_automatic_signing" clearable :placeholder="t('isAutomaticSigningPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in is_automatic_signingList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="coursesTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadCoursesList()">{{ 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="coursesTable.data" size="large" v-loading="coursesTable.loading">
<template #empty>
<span>{{ !coursesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="description" :label="t('description')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="target_group" :label="t('targetGroup')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="duration" :label="t('duration')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="taste_price" :label="t('tastePrice')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="price" :label="t('price')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('isAutomaticSigning')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in is_automatic_signingList">
<div v-if="item.value == row.is_automatic_signing">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column :label="t('status')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in statusList">
<div v-if="item.value == row.status">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="coursesTable.page" v-model:page-size="coursesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="coursesTable.total"
@size-change="loadCoursesList()" @current-change="loadCoursesList" />
</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 { getCoursesList, deleteCourses } from '@/addon/zhjw/api/courses'
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 coursesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"name":"",
"description":"",
"target_group":"",
"taste_price":"",
"price":"",
"is_automatic_signing":"",
"status":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const is_automatic_signingList = ref([] as any[])
const is_automatic_signingDictList = async () => {
is_automatic_signingList.value = await (await useDictionary('is_radio')).data.dictionary
}
is_automatic_signingDictList();
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('config_status')).data.dictionary
}
statusDictList();
/**
* 获取课程管理列表
*/
const loadCoursesList = (page: number = 1) => {
coursesTable.loading = true
coursesTable.page = page
getCoursesList({
page: coursesTable.page,
limit: coursesTable.limit,
...coursesTable.searchParam
}).then(res => {
coursesTable.loading = false
coursesTable.data = res.data.data
coursesTable.total = res.data.total
}).catch(() => {
coursesTable.loading = false
})
}
loadCoursesList()
const router = useRouter()
/**
* 添加课程管理
*/
const addEvent = () => {
router.push('/courses/courses_edit')
}
/**
* 编辑课程管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/courses/courses_edit?id='+data.id)
}
/**
* 删除课程管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('coursesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteCourses(id).then(() => {
loadCoursesList()
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadCoursesList()
}
</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>

252
admin/src/addon/zhjw/views/courses/courses_edit.vue

@ -0,0 +1,252 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<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('description')" prop="description">
<editor v-model="formData.description" />
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('targetGroup')" prop="target_group">
<el-input v-model="formData.target_group" clearable :placeholder="t('targetGroupPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('duration')" prop="duration">
<el-input v-model="formData.duration" clearable :placeholder="t('durationPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('tastePrice')" prop="taste_price">
<el-input v-model="formData.taste_price" clearable :placeholder="t('tastePricePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('price')" prop="price">
<el-input v-model="formData.price" clearable :placeholder="t('pricePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('isAutomaticSigning')" prop="is_automatic_signing">
<el-radio-group v-model="formData.is_automatic_signing" :placeholder="t('isAutomaticSigningPlaceholder')">
<el-radio
v-for="(item, index) in is_automatic_signingList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('automaticSigningTime')" prop="automatic_signing_time">
<el-input v-model="formData.automatic_signing_time" clearable :placeholder="t('automaticSigningTimePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getCoursesInfo,addCourses,editCourses } from '@/addon/zhjw/api/courses';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
name: '',
description: '',
thumbnail: '',
target_group: '',
duration: '',
taste_price: '',
price: '',
is_automatic_signing: '',
automatic_signing_time: 0,
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getCoursesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let is_automatic_signingList = ref([])
const is_automatic_signingDictList = async () => {
is_automatic_signingList.value = await (await useDictionary('is_radio')).data.dictionary
}
is_automatic_signingDictList();
watch(() => is_automatic_signingList.value, () => { formData.is_automatic_signing = is_automatic_signingList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('config_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
description: [
{ required: true, message: t('descriptionPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
target_group: [
{ required: true, message: t('targetGroupPlaceholder'), trigger: 'blur' },
]
,
duration: [
{ required: true, message: t('durationPlaceholder'), trigger: 'blur' },
]
,
taste_price: [
{ required: true, message: t('tastePricePlaceholder'), trigger: 'blur' },
]
,
price: [
{ required: true, message: t('pricePlaceholder'), trigger: 'blur' },
]
,
is_automatic_signing: [
{ required: true, message: t('isAutomaticSigningPlaceholder'), trigger: 'blur' },
]
,
automatic_signing_time: [
{ required: true, message: t('automaticSigningTimePlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editCourses : addCourses
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

179
admin/src/addon/zhjw/views/roles/roles.vue

@ -0,0 +1,179 @@
<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('addRoles') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="rolesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="name">
<el-select class="w-[280px]" v-model="rolesTable.searchParam.name" clearable :placeholder="t('namePlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in nameList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadRolesList()">{{ 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="rolesTable.data" size="large" v-loading="rolesTable.loading">
<template #empty>
<span>{{ !rolesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="id" :label="t('id')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('name')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in nameList">
<div v-if="item.value == row.name">{{ 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="rolesTable.page" v-model:page-size="rolesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="rolesTable.total"
@size-change="loadRolesList()" @current-change="loadRolesList" />
</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 { getRolesList, deleteRoles } from '@/addon/zhjw/api/roles'
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 rolesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"name":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const nameList = ref([] as any[])
const nameDictList = async () => {
nameList.value = await (await useDictionary('roles_name')).data.dictionary
}
nameDictList();
/**
* 获取角色管理列表
*/
const loadRolesList = (page: number = 1) => {
rolesTable.loading = true
rolesTable.page = page
getRolesList({
page: rolesTable.page,
limit: rolesTable.limit,
...rolesTable.searchParam
}).then(res => {
rolesTable.loading = false
rolesTable.data = res.data.data
rolesTable.total = res.data.total
}).catch(() => {
rolesTable.loading = false
})
}
loadRolesList()
const router = useRouter()
/**
* 添加角色管理
*/
const addEvent = () => {
router.push('/roles/roles_edit')
}
/**
* 编辑角色管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/roles/roles_edit?id='+data.id)
}
/**
* 删除角色管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('rolesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteRoles(id).then(() => {
loadRolesList()
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadRolesList()
}
</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>

171
admin/src/addon/zhjw/views/roles/roles_edit.vue

@ -0,0 +1,171 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('name')" prop="name">
<el-radio-group v-model="formData.name" :placeholder="t('namePlaceholder')">
<el-radio
v-for="(item, index) in nameList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('permissions')" prop="permissions">
<el-input v-model="formData.permissions" clearable :placeholder="t('permissionsPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('description')" >
<el-input v-model="formData.description" clearable :placeholder="t('descriptionPlaceholder')" class="input-width" />
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getRolesInfo,addRoles,editRoles } from '@/addon/zhjw/api/roles';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
name: '',
permissions: '',
description: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getRolesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let nameList = ref([])
const nameDictList = async () => {
nameList.value = await (await useDictionary('roles_name')).data.dictionary
}
nameDictList();
watch(() => nameList.value, () => { formData.name = nameList.value[0].value })
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
permissions: [
{ required: true, message: t('permissionsPlaceholder'), trigger: 'blur' },
]
,
description: [
{ required: true, message: t('descriptionPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editRoles : addRoles
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

248
admin/src/addon/zhjw/views/staff/staff.vue

@ -0,0 +1,248 @@
<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('addStaff') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="staffTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="name">
<el-input v-model="staffTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('gender')" prop="gender">
<el-select class="w-[280px]" v-model="staffTable.searchParam.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('phone')" prop="phone">
<el-input v-model="staffTable.searchParam.phone" :placeholder="t('phonePlaceholder')" />
</el-form-item>
<el-form-item :label="t('email')" prop="email">
<el-input v-model="staffTable.searchParam.email" :placeholder="t('emailPlaceholder')" />
</el-form-item>
<el-form-item :label="t('position')" prop="position">
<el-input v-model="staffTable.searchParam.position" :placeholder="t('positionPlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="staffTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('roleId')" prop="role_id">
<el-select class="w-[280px]" v-model="staffTable.searchParam.role_id" clearable :placeholder="t('roleIdPlaceholder')">
<el-option
v-for="(item, index) in roleIdList"
:key="index"
:label="item['description']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadStaffList()">{{ 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="staffTable.data" size="large" v-loading="staffTable.loading">
<template #empty>
<span>{{ !staffTable.loading ? t('emptyData') : '' }}</span>
</template>
<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="phone" :label="t('phone')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="email" :label="t('email')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="position" :label="t('position')" 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 prop="role_id_name" :label="t('roleId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('operation')" fixed="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="staffTable.page" v-model:page-size="staffTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="staffTable.total"
@size-change="loadStaffList()" @current-change="loadStaffList" />
</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 { getStaffList, deleteStaff, getWithRolesList } from '@/addon/zhjw/api/staff'
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 staffTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"name":"",
"gender":"",
"phone":"",
"email":"",
"position":"",
"status":"",
"role_id":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const genderList = ref([] as any[])
const genderDictList = async () => {
genderList.value = await (await useDictionary('sex')).data.dictionary
}
genderDictList();
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('zz_status')).data.dictionary
}
statusDictList();
/**
* 获取人员管理列表
*/
const loadStaffList = (page: number = 1) => {
staffTable.loading = true
staffTable.page = page
getStaffList({
page: staffTable.page,
limit: staffTable.limit,
...staffTable.searchParam
}).then(res => {
staffTable.loading = false
staffTable.data = res.data.data
staffTable.total = res.data.total
}).catch(() => {
staffTable.loading = false
})
}
loadStaffList()
const router = useRouter()
/**
* 添加人员管理
*/
const addEvent = () => {
router.push('/staff/staff_edit')
}
/**
* 编辑人员管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/staff/staff_edit?id='+data.id)
}
/**
* 删除人员管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('staffDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteStaff(id).then(() => {
loadStaffList()
}).catch(() => {
})
})
}
const roleIdList = ref([])
const setRoleIdList = async () => {
roleIdList.value = await (await getWithRolesList({})).data
}
setRoleIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadStaffList()
}
</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>

246
admin/src/addon/zhjw/views/staff/staff_edit.vue

@ -0,0 +1,246 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<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('header')">
<upload-image v-model="formData.header" />
</el-form-item>
<el-form-item :label="t('gender')" prop="gender">
<el-radio-group v-model="formData.gender" :placeholder="t('genderPlaceholder')">
<el-radio
v-for="(item, index) in genderList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('phone')" prop="phone">
<el-input v-model="formData.phone" clearable :placeholder="t('phonePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('email')" prop="email">
<el-input v-model="formData.email" clearable :placeholder="t('emailPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('position')" prop="position">
<el-input v-model="formData.position" clearable :placeholder="t('positionPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('roleId')" prop="role_id">
<el-radio-group v-model="formData.role_id" :placeholder="t('roleIdPlaceholder')">
<el-radio
v-for="(item, index) in roleIdList"
:key="index"
:label="item['id']">
{{ item['description'] }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getStaffInfo,addStaff,editStaff, getWithRolesList } from '@/addon/zhjw/api/staff';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
name: '',
header: '',
gender: '',
phone: '',
email: '',
position: '',
status: '',
role_id: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getStaffInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let genderList = ref([])
const genderDictList = async () => {
genderList.value = await (await useDictionary('sex')).data.dictionary
}
genderDictList();
watch(() => genderList.value, () => { formData.gender = genderList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('zz_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const roleIdList = ref([] as any[])
const setRoleIdList = async () => {
roleIdList.value = await (await getWithRolesList({})).data
}
setRoleIdList()
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
header: [
{ required: true, message: t('headerPlaceholder'), trigger: 'blur' },
]
,
gender: [
{ required: true, message: t('genderPlaceholder'), trigger: 'blur' },
]
,
phone: [
{ required: true, message: t('phonePlaceholder'), trigger: 'blur' },
]
,
email: [
{ required: true, message: t('emailPlaceholder'), trigger: 'blur' },
]
,
position: [
{ required: true, message: t('positionPlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
role_id: [
{ required: true, message: t('roleIdPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editStaff : addStaff
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

187
admin/src/addon/zhjw/views/timetables/components/timetables-edit.vue

@ -0,0 +1,187 @@
<template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updateTimetables') : t('addTimetables')" 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('classId')" prop="class_id">
<el-radio-group v-model="formData.class_id" :placeholder="t('classIdPlaceholder')">
<el-radio
v-for="(item, index) in classIdList"
:key="index"
:label="item['id']">
{{ item['name'] }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('scheduleJson')" prop="schedule_json">
<el-input v-model="formData.schedule_json" clearable :placeholder="t('scheduleJsonPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('cycle')" prop="cycle">
<el-radio-group v-model="formData.cycle" :placeholder="t('cyclePlaceholder')">
<el-radio
v-for="(item, index) in cycleList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
t('confirm')
}}</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { addTimetables, editTimetables, getTimetablesInfo, getWithClassesList } from '@/addon/zhjw/api/timetables'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
class_id: '',
schedule_json: '',
cycle: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
class_id: [
{ required: true, message: t('classIdPlaceholder'), trigger: 'blur' },
]
,
schedule_json: [
{ required: true, message: t('scheduleJsonPlaceholder'), trigger: 'blur' },
]
,
cycle: [
{ required: true, message: t('cyclePlaceholder'), trigger: 'blur' },
]
,
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editTimetables : addTimetables
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 cycleList = ref([])
const cycleDictList = async () => {
cycleList.value = await (await useDictionary('cycle')).data.dictionary
}
cycleDictList();
watch(() => cycleList.value, () => { formData.cycle = cycleList.value[0].value })
const classIdList = ref([] as any[])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassesList({})).data
}
setClassIdList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if(row){
const data = await (await getTimetablesInfo(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>

197
admin/src/addon/zhjw/views/timetables/timetables.vue

@ -0,0 +1,197 @@
<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('addTimetables') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="timetablesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('classId')" prop="class_id">
<el-select class="w-[280px]" v-model="timetablesTable.searchParam.class_id" clearable :placeholder="t('classIdPlaceholder')">
<el-option
v-for="(item, index) in classIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('cycle')" prop="cycle">
<el-select class="w-[280px]" v-model="timetablesTable.searchParam.cycle" clearable :placeholder="t('cyclePlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in cycleList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadTimetablesList()">{{ 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="timetablesTable.data" size="large" v-loading="timetablesTable.loading">
<template #empty>
<span>{{ !timetablesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="class_id_name" :label="t('classId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('cycle')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in cycleList">
<div v-if="item.value == row.cycle">{{ 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="timetablesTable.page" v-model:page-size="timetablesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="timetablesTable.total"
@size-change="loadTimetablesList()" @current-change="loadTimetablesList" />
</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 { getTimetablesList, deleteTimetables, getWithClassesList } from '@/addon/zhjw/api/timetables'
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 timetablesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"class_id":"",
"cycle":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const cycleList = ref([] as any[])
const cycleDictList = async () => {
cycleList.value = await (await useDictionary('cycle')).data.dictionary
}
cycleDictList();
/**
* 获取课表管理列表
*/
const loadTimetablesList = (page: number = 1) => {
timetablesTable.loading = true
timetablesTable.page = page
getTimetablesList({
page: timetablesTable.page,
limit: timetablesTable.limit,
...timetablesTable.searchParam
}).then(res => {
timetablesTable.loading = false
timetablesTable.data = res.data.data
timetablesTable.total = res.data.total
}).catch(() => {
timetablesTable.loading = false
})
}
loadTimetablesList()
const router = useRouter()
/**
* 添加课表管理
*/
const addEvent = () => {
router.push('/timetables/timetables_edit')
}
/**
* 编辑课表管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/timetables/timetables_edit?id='+data.id)
}
/**
* 删除课表管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('timetablesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteTimetables(id).then(() => {
loadTimetablesList()
}).catch(() => {
})
})
}
const classIdList = ref([])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassesList({})).data
}
setClassIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadTimetablesList()
}
</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>

184
admin/src/addon/zhjw/views/timetables/timetables_edit.vue

@ -0,0 +1,184 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('classId')" prop="class_id">
<el-radio-group v-model="formData.class_id" :placeholder="t('classIdPlaceholder')">
<el-radio
v-for="(item, index) in classIdList"
:key="index"
:label="item['id']">
{{ item['name'] }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('scheduleJson')" prop="schedule_json">
<el-input v-model="formData.schedule_json" clearable :placeholder="t('scheduleJsonPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('cycle')" prop="cycle">
<el-radio-group v-model="formData.cycle" :placeholder="t('cyclePlaceholder')">
<el-radio
v-for="(item, index) in cycleList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getTimetablesInfo,addTimetables,editTimetables, getWithClassesList } from '@/addon/zhjw/api/timetables';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
class_id: '',
schedule_json: '',
cycle: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getTimetablesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let cycleList = ref([])
const cycleDictList = async () => {
cycleList.value = await (await useDictionary('cycle')).data.dictionary
}
cycleDictList();
watch(() => cycleList.value, () => { formData.cycle = cycleList.value[0].value })
const classIdList = ref([] as any[])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassesList({})).data
}
setClassIdList()
//
const formRules = computed(() => {
return {
class_id: [
{ required: true, message: t('classIdPlaceholder'), trigger: 'blur' },
]
,
schedule_json: [
{ required: true, message: t('scheduleJsonPlaceholder'), trigger: 'blur' },
]
,
cycle: [
{ required: true, message: t('cyclePlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editTimetables : addTimetables
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

242
admin/src/addon/zhjw/views/venues/venues.vue

@ -0,0 +1,242 @@
<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('addVenues') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="venuesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('campusId')" prop="campus_id">
<el-select class="w-[280px]" v-model="venuesTable.searchParam.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
<el-option
v-for="(item, index) in campusIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('name')" prop="name">
<el-input v-model="venuesTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select class="w-[280px]" v-model="venuesTable.searchParam.type" clearable :placeholder="t('typePlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('capacity')" prop="capacity">
<el-input v-model="venuesTable.searchParam.capacity" :placeholder="t('capacityPlaceholder')" />
</el-form-item>
<el-form-item :label="t('availableTime')" prop="available_time">
<el-input v-model="venuesTable.searchParam.available_time" :placeholder="t('availableTimePlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="venuesTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadVenuesList()">{{ 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="venuesTable.data" size="large" v-loading="venuesTable.loading">
<template #empty>
<span>{{ !venuesTable.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="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('type')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in typeList">
<div v-if="item.value == row.type">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column prop="capacity" :label="t('capacity')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="available_time" :label="t('availableTime')" 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="venuesTable.page" v-model:page-size="venuesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="venuesTable.total"
@size-change="loadVenuesList()" @current-change="loadVenuesList" />
</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 { getVenuesList, deleteVenues, getWithCampusesList } from '@/addon/zhjw/api/venues'
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 venuesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"campus_id":"",
"name":"",
"type":"",
"capacity":"",
"available_time":"",
"status":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const typeList = ref([] as any[])
const typeDictList = async () => {
typeList.value = await (await useDictionary('cd_type')).data.dictionary
}
typeDictList();
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('cd_status')).data.dictionary
}
statusDictList();
/**
* 获取场地管理列表
*/
const loadVenuesList = (page: number = 1) => {
venuesTable.loading = true
venuesTable.page = page
getVenuesList({
page: venuesTable.page,
limit: venuesTable.limit,
...venuesTable.searchParam
}).then(res => {
venuesTable.loading = false
venuesTable.data = res.data.data
venuesTable.total = res.data.total
}).catch(() => {
venuesTable.loading = false
})
}
loadVenuesList()
const router = useRouter()
/**
* 添加场地管理
*/
const addEvent = () => {
router.push('/venues/venues_edit')
}
/**
* 编辑场地管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/venues/venues_edit?id='+data.id)
}
/**
* 删除场地管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('venuesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteVenues(id).then(() => {
loadVenuesList()
}).catch(() => {
})
})
}
const campusIdList = ref([])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusesList({})).data
}
setCampusIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadVenuesList()
}
</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>

236
admin/src/addon/zhjw/views/venues/venues_edit.vue

@ -0,0 +1,236 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('campusId')" prop="campus_id">
<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['name']"
:value="item['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('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-radio-group v-model="formData.type" :placeholder="t('typePlaceholder')">
<el-radio
v-for="(item, index) in typeList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('capacity')" prop="capacity">
<el-input v-model="formData.capacity" clearable :placeholder="t('capacityPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('availableTime')" prop="available_time">
<el-input v-model="formData.available_time" clearable :placeholder="t('availableTimePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getVenuesInfo,addVenues,editVenues, getWithCampusesList } from '@/addon/zhjw/api/venues';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
campus_id: '',
name: '',
thumbnail: '',
type: '',
capacity: 0,
available_time: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getVenuesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('cd_type')).data.dictionary
}
typeDictList();
watch(() => typeList.value, () => { formData.type = typeList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('cd_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 getWithCampusesList({})).data
}
setCampusIdList()
//
const formRules = computed(() => {
return {
campus_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
]
,
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
type: [
{ required: true, message: t('typePlaceholder'), trigger: 'blur' },
]
,
capacity: [
{ required: true, message: t('capacityPlaceholder'), trigger: 'blur' },
]
,
available_time: [
{ required: true, message: t('availableTimePlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editVenues : addVenues
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

2
niucloud/.env

@ -1 +1 @@
APP_DEBUG = true [APP] DEFAULT_TIMEZONE = Asia/Shanghai AUTH_KEY = ymektwfaxgsdigfpbnjoruhczasdvhql PRODUCT_KEY = {product_key} [DATABASE] TYPE = mysql HOSTNAME = 127.0.0.1 DATABASE = school_oa USERNAME = root PASSWORD = root HOSTPORT = 3306 PREFIX = school_ CHARSET = utf8mb4 DEBUG = false [REDIS] REDIS_HOSTNAME = 127.0.0.1 PORT = 6379 REDIS_PASSWORD = SELECT = 0 [QUEUE] state = false [LANG] default_lang = zh-cn [SYSTEM] ADMIN_TOKEN_NAME = token API_TOKEN_NAME = token ADMIN_TOKEN_EXPIRE_TIME = 604800 API_TOKEN_EXPIRE_TIME = 86400 LANG_NAME = lang CHANNEL_NAME = channel ADMIN_DOMAIN = WAP_DOMAIN = WEB_DOMAIN = [NIUCLOUD] code = secret = APP_DEBUG = true [APP] DEFAULT_TIMEZONE = Asia/Shanghai AUTH_KEY = ymektwfaxgsdigfpbnjoruhczasdvhql PRODUCT_KEY = {product_key} [DATABASE] TYPE = mysql HOSTNAME = 146.56.228.75 DATABASE = school_oa USERNAME = school_oa PASSWORD = xseii2aBWjmSSBpB HOSTPORT = 3306 PREFIX = school_ CHARSET = utf8mb4 DEBUG = false [REDIS] REDIS_HOSTNAME = 127.0.0.1 PORT = 6379 REDIS_PASSWORD = SELECT = 0 [QUEUE] state = false [LANG] default_lang = zh-cn [SYSTEM] ADMIN_TOKEN_NAME = token API_TOKEN_NAME = token ADMIN_TOKEN_EXPIRE_TIME = 604800 API_TOKEN_EXPIRE_TIME = 86400 LANG_NAME = lang CHANNEL_NAME = channel ADMIN_DOMAIN = WAP_DOMAIN = WEB_DOMAIN = [NIUCLOUD] code = secret =

58
niucloud/addon/zhjw/admin/api/campuses.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- campuses
/**
*
* @param params
* @returns
*/
export function getCampusesList(params: Record<string, any>) {
return request.get(`zhjw/campuses`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getCampusesInfo(id: number) {
return request.get(`zhjw/campuses/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addCampuses(params: Record<string, any>) {
return request.post('zhjw/campuses', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editCampuses(params: Record<string, any>) {
return request.put(`zhjw/campuses/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteCampuses(id: number) {
return request.delete(`zhjw/campuses/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
// USER_CODE_END -- campuses

58
niucloud/addon/zhjw/admin/api/classes.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- classes
/**
*
* @param params
* @returns
*/
export function getClassesList(params: Record<string, any>) {
return request.get(`zhjw/classes`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getClassesInfo(id: number) {
return request.get(`zhjw/classes/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addClasses(params: Record<string, any>) {
return request.post('zhjw/classes', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editClasses(params: Record<string, any>) {
return request.put(`zhjw/classes/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteClasses(id: number) {
return request.delete(`zhjw/classes/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithVenuesList(params: Record<string,any>){
return request.get('zhjw/venues_all', {params})
}
// USER_CODE_END -- classes

54
niucloud/addon/zhjw/admin/api/courses.ts

@ -0,0 +1,54 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- courses
/**
*
* @param params
* @returns
*/
export function getCoursesList(params: Record<string, any>) {
return request.get(`zhjw/courses`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getCoursesInfo(id: number) {
return request.get(`zhjw/courses/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addCourses(params: Record<string, any>) {
return request.post('zhjw/courses', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editCourses(params: Record<string, any>) {
return request.put(`zhjw/courses/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteCourses(id: number) {
return request.delete(`zhjw/courses/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
// USER_CODE_END -- courses

58
niucloud/addon/zhjw/admin/api/roles.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- roles
/**
*
* @param params
* @returns
*/
export function getRolesList(params: Record<string, any>) {
return request.get(`zhjw/roles`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getRolesInfo(id: number) {
return request.get(`zhjw/roles/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addRoles(params: Record<string, any>) {
return request.post('zhjw/roles', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editRoles(params: Record<string, any>) {
return request.put(`zhjw/roles/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteRoles(id: number) {
return request.delete(`zhjw/roles/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
// USER_CODE_END -- roles

56
niucloud/addon/zhjw/admin/api/staff.ts

@ -0,0 +1,56 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- staff
/**
*
* @param params
* @returns
*/
export function getStaffList(params: Record<string, any>) {
return request.get(`zhjw/staff`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getStaffInfo(id: number) {
return request.get(`zhjw/staff/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addStaff(params: Record<string, any>) {
return request.post('zhjw/staff', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editStaff(params: Record<string, any>) {
return request.put(`zhjw/staff/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteStaff(id: number) {
return request.delete(`zhjw/staff/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithRolesList(params: Record<string,any>){
return request.get('zhjw/roles_all', {params})
}
// USER_CODE_END -- staff

58
niucloud/addon/zhjw/admin/api/timetables.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- timetables
/**
*
* @param params
* @returns
*/
export function getTimetablesList(params: Record<string, any>) {
return request.get(`zhjw/timetables`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getTimetablesInfo(id: number) {
return request.get(`zhjw/timetables/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addTimetables(params: Record<string, any>) {
return request.post('zhjw/timetables', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editTimetables(params: Record<string, any>) {
return request.put(`zhjw/timetables/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteTimetables(id: number) {
return request.delete(`zhjw/timetables/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithClassesList(params: Record<string,any>){
return request.get('zhjw/classes_all', {params})
}
// USER_CODE_END -- timetables

58
niucloud/addon/zhjw/admin/api/venues.ts

@ -0,0 +1,58 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- venues
/**
*
* @param params
* @returns
*/
export function getVenuesList(params: Record<string, any>) {
return request.get(`zhjw/venues`, {params})
}
/**
*
* @param id id
* @returns
*/
export function getVenuesInfo(id: number) {
return request.get(`zhjw/venues/${id}`);
}
/**
*
* @param params
* @returns
*/
export function addVenues(params: Record<string, any>) {
return request.post('zhjw/venues', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editVenues(params: Record<string, any>) {
return request.put(`zhjw/venues/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @returns
*/
export function deleteVenues(id: number) {
return request.delete(`zhjw/venues/${id}`, { showErrorMessage: true, showSuccessMessage: true })
}
export function getWithCampusesList(params: Record<string,any>){
return request.get('zhjw/campuses_all', {params})
}
// USER_CODE_END -- venues

19
niucloud/addon/zhjw/admin/lang/zh-cn/campuses.campuses.json

@ -0,0 +1,19 @@
{
"name":"校区名称",
"namePlaceholder":"请输入校区名称",
"coordinate":"坐标地址",
"coordinatePlaceholder":"请输入坐标地址",
"address":"校区地址",
"addressPlaceholder":"请输入校区地址",
"contactPerson":"联系人",
"contactPersonPlaceholder":"请输入联系人",
"contactPhone":"联系电话",
"contactPhonePlaceholder":"请输入联系电话",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addCampuses":"添加校区管理",
"updateCampuses":"编辑校区管理",
"campusesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

21
niucloud/addon/zhjw/admin/lang/zh-cn/campuses.campuses_edit.json

@ -0,0 +1,21 @@
{
"name":"校区名称",
"coordinate":"坐标地址",
"address":"校区地址",
"contactPerson":"联系人",
"contactPhone":"联系电话",
"status":"状态",
"thumbnail":"缩略图",
"description":"校区描述",
"namePlaceholder":"请输入校区名称",
"coordinatePlaceholder":"请输入坐标地址",
"addressPlaceholder":"请输入校区地址",
"contactPersonPlaceholder":"请输入联系人",
"contactPhonePlaceholder":"请输入联系电话",
"statusPlaceholder":"请输入状态",
"thumbnailPlaceholder":"请上传缩略图",
"descriptionPlaceholder":"请输入校区描述",
"addCampuses":"添加校区管理",
"updateCampuses":"编辑校区管理",
"campusesDeleteTips":"确定要删除该校区管理吗?"
}

19
niucloud/addon/zhjw/admin/lang/zh-cn/classes.classes.json

@ -0,0 +1,19 @@
{
"venueId":"所属场地",
"venueIdPlaceholder":"全部",
"name":"班级名称",
"namePlaceholder":"请输入班级名称",
"maxStudents":"最大学员数",
"maxStudentsPlaceholder":"请输入最大学员数",
"startDate":"开班时间",
"startDatePlaceholder":"请输入开班时间",
"endDate":"结班时间",
"endDatePlaceholder":"请输入结班时间",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addClasses":"添加班级管理",
"updateClasses":"编辑班级管理",
"classesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

19
niucloud/addon/zhjw/admin/lang/zh-cn/classes.classes_edit.json

@ -0,0 +1,19 @@
{
"venueId":"所属场地",
"thumbnail":"缩略图",
"name":"班级名称",
"maxStudents":"最大学员数",
"startDate":"开班时间",
"endDate":"结班时间",
"status":"状态",
"venueIdPlaceholder":"请输入所属场地",
"thumbnailPlaceholder":"请上传缩略图",
"namePlaceholder":"请输入班级名称",
"maxStudentsPlaceholder":"请输入最大学员数",
"startDatePlaceholder":"请选择开班时间",
"endDatePlaceholder":"请选择结班时间",
"statusPlaceholder":"请输入状态",
"addClasses":"添加班级管理",
"updateClasses":"编辑班级管理",
"classesDeleteTips":"确定要删除该班级管理吗?"
}

22
niucloud/addon/zhjw/admin/lang/zh-cn/courses.courses.json

@ -0,0 +1,22 @@
{
"name":"课程名称",
"namePlaceholder":"请输入课程名称",
"description":"课程描述",
"descriptionPlaceholder":"请输入课程描述",
"targetGroup":"适合人群",
"targetGroupPlaceholder":"请输入适合人群",
"duration":"课时数",
"tastePrice":"体验价格",
"tastePricePlaceholder":"请输入体验价格",
"price":"正式价格",
"pricePlaceholder":"请输入正式价格",
"isAutomaticSigning":"是否自动签约",
"isAutomaticSigningPlaceholder":"请输入是否自动签约",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addCourses":"添加课程管理",
"updateCourses":"编辑课程管理",
"coursesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

25
niucloud/addon/zhjw/admin/lang/zh-cn/courses.courses_edit.json

@ -0,0 +1,25 @@
{
"name":"课程名称",
"description":"课程描述",
"thumbnail":"缩略图",
"targetGroup":"适合人群",
"duration":"课时数",
"tastePrice":"体验价格",
"price":"正式价格",
"isAutomaticSigning":"是否自动签约",
"automaticSigningTime":"自动签约",
"status":"状态",
"namePlaceholder":"请输入课程名称",
"descriptionPlaceholder":"请输入课程描述",
"thumbnailPlaceholder":"请上传缩略图",
"targetGroupPlaceholder":"请输入适合人群",
"durationPlaceholder":"请输入课时数",
"tastePricePlaceholder":"请输入体验价格",
"pricePlaceholder":"请输入正式价格",
"isAutomaticSigningPlaceholder":"请输入是否自动签约",
"automaticSigningTimePlaceholder":"请输入自动签约",
"statusPlaceholder":"请输入状态",
"addCourses":"添加课程管理",
"updateCourses":"编辑课程管理",
"coursesDeleteTips":"确定要删除该课程管理吗?"
}

10
niucloud/addon/zhjw/admin/lang/zh-cn/roles.roles.json

@ -0,0 +1,10 @@
{
"id":"ID",
"name":"角色名称",
"namePlaceholder":"请输入角色名称",
"addRoles":"添加角色管理",
"updateRoles":"编辑角色管理",
"rolesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

11
niucloud/addon/zhjw/admin/lang/zh-cn/roles.roles_edit.json

@ -0,0 +1,11 @@
{
"name":"角色名称",
"permissions":"数据权限配置",
"description":"角色描述",
"namePlaceholder":"请输入角色名称",
"permissionsPlaceholder":"请输入数据权限配置",
"descriptionPlaceholder":"请输入角色描述",
"addRoles":"添加角色管理",
"updateRoles":"编辑角色管理",
"rolesDeleteTips":"确定要删除该角色管理吗?"
}

21
niucloud/addon/zhjw/admin/lang/zh-cn/staff.staff.json

@ -0,0 +1,21 @@
{
"name":"姓名",
"namePlaceholder":"请输入姓名",
"gender":"性别",
"genderPlaceholder":"请输入性别",
"phone":"联系方式",
"phonePlaceholder":"请输入联系方式",
"email":"邮箱",
"emailPlaceholder":"请输入邮箱",
"position":"职位",
"positionPlaceholder":"请输入职位",
"status":"状态",
"statusPlaceholder":"请输入状态",
"roleId":"角色关系",
"roleIdPlaceholder":"全部",
"addStaff":"添加人员管理",
"updateStaff":"编辑人员管理",
"staffDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

21
niucloud/addon/zhjw/admin/lang/zh-cn/staff.staff_edit.json

@ -0,0 +1,21 @@
{
"name":"姓名",
"header":"头像",
"gender":"性别",
"phone":"联系方式",
"email":"邮箱",
"position":"职位",
"status":"状态",
"roleId":"角色关系",
"namePlaceholder":"请输入姓名",
"headerPlaceholder":"请上传头像",
"genderPlaceholder":"请输入性别",
"phonePlaceholder":"请输入联系方式",
"emailPlaceholder":"请输入邮箱",
"positionPlaceholder":"请输入职位",
"statusPlaceholder":"请输入状态",
"roleIdPlaceholder":"请输入角色关系",
"addStaff":"添加人员管理",
"updateStaff":"编辑人员管理",
"staffDeleteTips":"确定要删除该人员管理吗?"
}

11
niucloud/addon/zhjw/admin/lang/zh-cn/timetables.timetables.json

@ -0,0 +1,11 @@
{
"classId":"所属班级",
"classIdPlaceholder":"全部",
"cycle":"周期",
"cyclePlaceholder":"请输入周期",
"addTimetables":"添加课表管理",
"updateTimetables":"编辑课表管理",
"timetablesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

11
niucloud/addon/zhjw/admin/lang/zh-cn/timetables.timetables_edit.json

@ -0,0 +1,11 @@
{
"classId":"所属班级",
"scheduleJson":"排课详情",
"cycle":"周期",
"classIdPlaceholder":"请输入所属班级",
"scheduleJsonPlaceholder":"请输入排课详情",
"cyclePlaceholder":"请输入周期",
"addTimetables":"添加课表管理",
"updateTimetables":"编辑课表管理",
"timetablesDeleteTips":"确定要删除该课表管理吗?"
}

19
niucloud/addon/zhjw/admin/lang/zh-cn/venues.venues.json

@ -0,0 +1,19 @@
{
"campusId":"所属校区",
"campusIdPlaceholder":"全部",
"name":"场地名称",
"namePlaceholder":"请输入场地名称",
"type":"场地类型",
"typePlaceholder":"请输入场地类型",
"capacity":"容纳人数",
"capacityPlaceholder":"请输入容纳人数",
"availableTime":"可用时间段",
"availableTimePlaceholder":"请输入可用时间段",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addVenues":"添加场地管理",
"updateVenues":"编辑场地管理",
"venuesDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

19
niucloud/addon/zhjw/admin/lang/zh-cn/venues.venues_edit.json

@ -0,0 +1,19 @@
{
"campusId":"所属校区",
"name":"场地名称",
"thumbnail":"缩略图",
"type":"场地类型",
"capacity":"容纳人数",
"availableTime":"可用时间段",
"status":"状态",
"campusIdPlaceholder":"请选择所属校区",
"namePlaceholder":"请输入场地名称",
"thumbnailPlaceholder":"请上传缩略图",
"typePlaceholder":"请输入场地类型",
"capacityPlaceholder":"请输入容纳人数",
"availableTimePlaceholder":"请输入可用时间段",
"statusPlaceholder":"请输入状态",
"addVenues":"添加场地管理",
"updateVenues":"编辑场地管理",
"venuesDeleteTips":"确定要删除该场地管理吗?"
}

207
niucloud/addon/zhjw/admin/views/campuses/campuses.vue

@ -0,0 +1,207 @@
<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('addCampuses') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="campusesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="name">
<el-input v-model="campusesTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('coordinate')" prop="coordinate">
<el-input v-model="campusesTable.searchParam.coordinate" :placeholder="t('coordinatePlaceholder')" />
</el-form-item>
<el-form-item :label="t('address')" prop="address">
<el-input v-model="campusesTable.searchParam.address" :placeholder="t('addressPlaceholder')" />
</el-form-item>
<el-form-item :label="t('contactPerson')" prop="contact_person">
<el-input v-model="campusesTable.searchParam.contact_person" :placeholder="t('contactPersonPlaceholder')" />
</el-form-item>
<el-form-item :label="t('contactPhone')" prop="contact_phone">
<el-input v-model="campusesTable.searchParam.contact_phone" :placeholder="t('contactPhonePlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="campusesTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadCampusesList()">{{ 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="campusesTable.data" size="large" v-loading="campusesTable.loading">
<template #empty>
<span>{{ !campusesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="coordinate" :label="t('coordinate')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="address" :label="t('address')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="contact_person" :label="t('contactPerson')" 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 :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="campusesTable.page" v-model:page-size="campusesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="campusesTable.total"
@size-change="loadCampusesList()" @current-change="loadCampusesList" />
</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 { getCampusesList, deleteCampuses } from '@/addon/zhjw/api/campuses'
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 campusesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"name":"",
"coordinate":"",
"address":"",
"contact_person":"",
"contact_phone":"",
"status":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('is_radio')).data.dictionary
}
statusDictList();
/**
* 获取校区管理列表
*/
const loadCampusesList = (page: number = 1) => {
campusesTable.loading = true
campusesTable.page = page
getCampusesList({
page: campusesTable.page,
limit: campusesTable.limit,
...campusesTable.searchParam
}).then(res => {
campusesTable.loading = false
campusesTable.data = res.data.data
campusesTable.total = res.data.total
}).catch(() => {
campusesTable.loading = false
})
}
loadCampusesList()
const router = useRouter()
/**
* 添加校区管理
*/
const addEvent = () => {
router.push('/campuses/campuses_edit')
}
/**
* 编辑校区管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/campuses/campuses_edit?id='+data.id)
}
/**
* 删除校区管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('campusesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteCampuses(id).then(() => {
loadCampusesList()
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadCampusesList()
}
</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>

221
niucloud/addon/zhjw/admin/views/campuses/campuses_edit.vue

@ -0,0 +1,221 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<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('coordinate')" prop="coordinate">
<el-input v-model="formData.coordinate" clearable :placeholder="t('coordinatePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('address')" prop="address">
<el-input v-model="formData.address" clearable :placeholder="t('addressPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('contactPerson')" prop="contact_person">
<el-input v-model="formData.contact_person" clearable :placeholder="t('contactPersonPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('contactPhone')" prop="contact_phone">
<el-input v-model="formData.contact_phone" clearable :placeholder="t('contactPhonePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('description')" prop="description">
<el-input v-model="formData.description" clearable :placeholder="t('descriptionPlaceholder')" class="input-width" />
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getCampusesInfo,addCampuses,editCampuses } from '@/addon/zhjw/api/campuses';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
name: '',
coordinate: '',
address: '',
contact_person: '',
contact_phone: '',
status: '',
thumbnail: '',
description: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getCampusesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('is_radio')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
coordinate: [
{ required: true, message: t('coordinatePlaceholder'), trigger: 'blur' },
]
,
address: [
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' },
]
,
contact_person: [
{ required: true, message: t('contactPersonPlaceholder'), trigger: 'blur' },
]
,
contact_phone: [
{ required: true, message: t('contactPhonePlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
description: [
{ required: true, message: t('descriptionPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editCampuses : addCampuses
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

225
niucloud/addon/zhjw/admin/views/classes/classes.vue

@ -0,0 +1,225 @@
<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('addClasses') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="classesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('venueId')" prop="venue_id">
<el-select class="w-[280px]" v-model="classesTable.searchParam.venue_id" clearable :placeholder="t('venueIdPlaceholder')">
<el-option
v-for="(item, index) in venueIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('name')" prop="name">
<el-input v-model="classesTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('maxStudents')" prop="max_students">
<el-input v-model="classesTable.searchParam.max_students" :placeholder="t('maxStudentsPlaceholder')" />
</el-form-item>
<el-form-item :label="t('startDate')" prop="start_date">
<el-date-picker v-model="classesTable.searchParam.start_date" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item :label="t('endDate')" prop="end_date">
<el-date-picker v-model="classesTable.searchParam.end_date" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="classesTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadClassesList()">{{ 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="classesTable.data" size="large" v-loading="classesTable.loading">
<template #empty>
<span>{{ !classesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="venue_id_name" :label="t('venueId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="max_students" :label="t('maxStudents')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="start_date" :label="t('startDate')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="end_date" :label="t('endDate')" 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="classesTable.page" v-model:page-size="classesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="classesTable.total"
@size-change="loadClassesList()" @current-change="loadClassesList" />
</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 { getClassesList, deleteClasses, getWithVenuesList } from '@/addon/zhjw/api/classes'
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 classesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"venue_id":"",
"name":"",
"max_students":"",
"start_date":"",
"end_date":"",
"status":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('bj_status')).data.dictionary
}
statusDictList();
/**
* 获取班级管理列表
*/
const loadClassesList = (page: number = 1) => {
classesTable.loading = true
classesTable.page = page
getClassesList({
page: classesTable.page,
limit: classesTable.limit,
...classesTable.searchParam
}).then(res => {
classesTable.loading = false
classesTable.data = res.data.data
classesTable.total = res.data.total
}).catch(() => {
classesTable.loading = false
})
}
loadClassesList()
const router = useRouter()
/**
* 添加班级管理
*/
const addEvent = () => {
router.push('/classes/classes_edit')
}
/**
* 编辑班级管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/classes/classes_edit?id='+data.id)
}
/**
* 删除班级管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('classesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteClasses(id).then(() => {
loadClassesList()
}).catch(() => {
})
})
}
const venueIdList = ref([])
const setVenueIdList = async () => {
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadClassesList()
}
</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>

236
niucloud/addon/zhjw/admin/views/classes/classes_edit.vue

@ -0,0 +1,236 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('venueId')" prop="venue_id">
<el-radio-group v-model="formData.venue_id" :placeholder="t('venueIdPlaceholder')">
<el-radio
v-for="(item, index) in venueIdList"
:key="index"
:label="item['id']">
{{ item['name'] }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</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('maxStudents')" prop="max_students">
<el-input v-model="formData.max_students" clearable :placeholder="t('maxStudentsPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('startDate')" prop="start_date" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.start_date"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('startDatePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('endDate')" prop="end_date" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.end_date"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('endDatePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getClassesInfo,addClasses,editClasses, getWithVenuesList } from '@/addon/zhjw/api/classes';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
venue_id: '',
thumbnail: '',
name: '',
max_students: 0,
start_date: '',
end_date: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getClassesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('bj_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const venueIdList = ref([] as any[])
const setVenueIdList = async () => {
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
//
const formRules = computed(() => {
return {
venue_id: [
{ required: true, message: t('venueIdPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
max_students: [
{ required: true, message: t('maxStudentsPlaceholder'), trigger: 'blur' },
]
,
start_date: [
{ required: true, message: t('startDatePlaceholder'), trigger: 'blur' },
]
,
end_date: [
{ required: true, message: t('endDatePlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editClasses : addClasses
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

249
niucloud/addon/zhjw/admin/views/classes/components/classes-edit.vue

@ -0,0 +1,249 @@
<template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updateClasses') : t('addClasses')" 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('venueId')" prop="venue_id">
<el-select class="input-width" v-model="formData.venue_id" clearable :placeholder="t('venueIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in venueIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('timetableId')" prop="timetable_id">
<el-input v-model="formData.timetable_id" clearable :placeholder="t('timetableIdPlaceholder')" class="input-width" />
</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('maxStudents')" prop="max_students">
<el-input v-model="formData.max_students" clearable :placeholder="t('maxStudentsPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('startDate')" prop="start_date" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.start_date"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('startDatePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('endDate')" prop="end_date" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.end_date"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('endDatePlaceholder')">
</el-date-picker>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<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 { addClasses, editClasses, getClassesInfo, getWithVenuesList } from '@/addon/zhjw/api/classes'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
venue_id: '',
thumbnail: '',
timetable_id: '',
name: '',
max_students: '',
start_date: '',
end_date: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
venue_id: [
{ required: true, message: t('venueIdPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
timetable_id: [
{ required: true, message: t('timetableIdPlaceholder'), trigger: 'blur' },
]
,
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
max_students: [
{ required: true, message: t('maxStudentsPlaceholder'), trigger: 'blur' },
]
,
start_date: [
{ required: true, message: t('startDatePlaceholder'), trigger: 'blur' },
]
,
end_date: [
{ required: true, message: t('endDatePlaceholder'), 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 ? editClasses : addClasses
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 statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('bj_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const venueIdList = ref([] as any[])
const setVenueIdList = async () => {
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if(row){
const data = await (await getClassesInfo(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>

255
niucloud/addon/zhjw/admin/views/courses/components/courses-edit.vue

@ -0,0 +1,255 @@
<template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updateCourses') : t('addCourses')" 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('name')" prop="name">
<el-input v-model="formData.name" clearable :placeholder="t('namePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('description')" prop="description">
<editor v-model="formData.description" />
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('targetGroup')" prop="target_group">
<el-input v-model="formData.target_group" clearable :placeholder="t('targetGroupPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('duration')" prop="duration">
<el-input v-model="formData.duration" clearable :placeholder="t('durationPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('tastePrice')" prop="taste_price">
<el-input v-model="formData.taste_price" clearable :placeholder="t('tastePricePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('price')" prop="price">
<el-input v-model="formData.price" clearable :placeholder="t('pricePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('isAutomaticSigning')" prop="is_automatic_signing">
<el-radio-group v-model="formData.is_automatic_signing" :placeholder="t('isAutomaticSigningPlaceholder')">
<el-radio
v-for="(item, index) in is_automatic_signingList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('automaticSigningTime')" prop="automatic_signing_time">
<el-input v-model="formData.automatic_signing_time" clearable :placeholder="t('automaticSigningTimePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<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 { addCourses, editCourses, getCoursesInfo } from '@/addon/zhjw/api/courses'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
name: '',
description: '',
thumbnail: '',
target_group: '',
duration: '',
taste_price: '',
price: '',
is_automatic_signing: '',
automatic_signing_time: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
description: [
{ required: true, message: t('descriptionPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
target_group: [
{ required: true, message: t('targetGroupPlaceholder'), trigger: 'blur' },
]
,
duration: [
{ required: true, message: t('durationPlaceholder'), trigger: 'blur' },
]
,
taste_price: [
{ required: true, message: t('tastePricePlaceholder'), trigger: 'blur' },
]
,
price: [
{ required: true, message: t('pricePlaceholder'), trigger: 'blur' },
]
,
is_automatic_signing: [
{ required: true, message: t('isAutomaticSigningPlaceholder'), trigger: 'blur' },
]
,
automatic_signing_time: [
{ required: true, message: t('automaticSigningTimePlaceholder'), 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 ? editCourses : addCourses
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 is_automatic_signingList = ref([])
const is_automatic_signingDictList = async () => {
is_automatic_signingList.value = await (await useDictionary('is_radio')).data.dictionary
}
is_automatic_signingDictList();
watch(() => is_automatic_signingList.value, () => { formData.is_automatic_signing = is_automatic_signingList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('config_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if(row){
const data = await (await getCoursesInfo(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>

233
niucloud/addon/zhjw/admin/views/courses/courses.vue

@ -0,0 +1,233 @@
<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('addCourses') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="coursesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="name">
<el-input v-model="coursesTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('targetGroup')" prop="target_group">
<el-input v-model="coursesTable.searchParam.target_group" :placeholder="t('targetGroupPlaceholder')" />
</el-form-item>
<el-form-item :label="t('tastePrice')" prop="taste_price">
<el-input v-model="coursesTable.searchParam.taste_price" :placeholder="t('tastePricePlaceholder')" />
</el-form-item>
<el-form-item :label="t('price')" prop="price">
<el-input v-model="coursesTable.searchParam.price" :placeholder="t('pricePlaceholder')" />
</el-form-item>
<el-form-item :label="t('isAutomaticSigning')" prop="is_automatic_signing">
<el-select class="w-[280px]" v-model="coursesTable.searchParam.is_automatic_signing" clearable :placeholder="t('isAutomaticSigningPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in is_automatic_signingList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="coursesTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadCoursesList()">{{ 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="coursesTable.data" size="large" v-loading="coursesTable.loading">
<template #empty>
<span>{{ !coursesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="description" :label="t('description')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="target_group" :label="t('targetGroup')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="duration" :label="t('duration')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="taste_price" :label="t('tastePrice')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="price" :label="t('price')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('isAutomaticSigning')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in is_automatic_signingList">
<div v-if="item.value == row.is_automatic_signing">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column :label="t('status')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in statusList">
<div v-if="item.value == row.status">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="coursesTable.page" v-model:page-size="coursesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="coursesTable.total"
@size-change="loadCoursesList()" @current-change="loadCoursesList" />
</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 { getCoursesList, deleteCourses } from '@/addon/zhjw/api/courses'
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 coursesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"name":"",
"description":"",
"target_group":"",
"taste_price":"",
"price":"",
"is_automatic_signing":"",
"status":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const is_automatic_signingList = ref([] as any[])
const is_automatic_signingDictList = async () => {
is_automatic_signingList.value = await (await useDictionary('is_radio')).data.dictionary
}
is_automatic_signingDictList();
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('config_status')).data.dictionary
}
statusDictList();
/**
* 获取课程管理列表
*/
const loadCoursesList = (page: number = 1) => {
coursesTable.loading = true
coursesTable.page = page
getCoursesList({
page: coursesTable.page,
limit: coursesTable.limit,
...coursesTable.searchParam
}).then(res => {
coursesTable.loading = false
coursesTable.data = res.data.data
coursesTable.total = res.data.total
}).catch(() => {
coursesTable.loading = false
})
}
loadCoursesList()
const router = useRouter()
/**
* 添加课程管理
*/
const addEvent = () => {
router.push('/courses/courses_edit')
}
/**
* 编辑课程管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/courses/courses_edit?id='+data.id)
}
/**
* 删除课程管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('coursesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteCourses(id).then(() => {
loadCoursesList()
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadCoursesList()
}
</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>

252
niucloud/addon/zhjw/admin/views/courses/courses_edit.vue

@ -0,0 +1,252 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<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('description')" prop="description">
<editor v-model="formData.description" />
</el-form-item>
<el-form-item :label="t('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('targetGroup')" prop="target_group">
<el-input v-model="formData.target_group" clearable :placeholder="t('targetGroupPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('duration')" prop="duration">
<el-input v-model="formData.duration" clearable :placeholder="t('durationPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('tastePrice')" prop="taste_price">
<el-input v-model="formData.taste_price" clearable :placeholder="t('tastePricePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('price')" prop="price">
<el-input v-model="formData.price" clearable :placeholder="t('pricePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('isAutomaticSigning')" prop="is_automatic_signing">
<el-radio-group v-model="formData.is_automatic_signing" :placeholder="t('isAutomaticSigningPlaceholder')">
<el-radio
v-for="(item, index) in is_automatic_signingList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('automaticSigningTime')" prop="automatic_signing_time">
<el-input v-model="formData.automatic_signing_time" clearable :placeholder="t('automaticSigningTimePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getCoursesInfo,addCourses,editCourses } from '@/addon/zhjw/api/courses';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
name: '',
description: '',
thumbnail: '',
target_group: '',
duration: '',
taste_price: '',
price: '',
is_automatic_signing: '',
automatic_signing_time: 0,
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getCoursesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let is_automatic_signingList = ref([])
const is_automatic_signingDictList = async () => {
is_automatic_signingList.value = await (await useDictionary('is_radio')).data.dictionary
}
is_automatic_signingDictList();
watch(() => is_automatic_signingList.value, () => { formData.is_automatic_signing = is_automatic_signingList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('config_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
description: [
{ required: true, message: t('descriptionPlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
target_group: [
{ required: true, message: t('targetGroupPlaceholder'), trigger: 'blur' },
]
,
duration: [
{ required: true, message: t('durationPlaceholder'), trigger: 'blur' },
]
,
taste_price: [
{ required: true, message: t('tastePricePlaceholder'), trigger: 'blur' },
]
,
price: [
{ required: true, message: t('pricePlaceholder'), trigger: 'blur' },
]
,
is_automatic_signing: [
{ required: true, message: t('isAutomaticSigningPlaceholder'), trigger: 'blur' },
]
,
automatic_signing_time: [
{ required: true, message: t('automaticSigningTimePlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editCourses : addCourses
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

179
niucloud/addon/zhjw/admin/views/roles/roles.vue

@ -0,0 +1,179 @@
<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('addRoles') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="rolesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="name">
<el-select class="w-[280px]" v-model="rolesTable.searchParam.name" clearable :placeholder="t('namePlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in nameList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadRolesList()">{{ 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="rolesTable.data" size="large" v-loading="rolesTable.loading">
<template #empty>
<span>{{ !rolesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="id" :label="t('id')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('name')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in nameList">
<div v-if="item.value == row.name">{{ 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="rolesTable.page" v-model:page-size="rolesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="rolesTable.total"
@size-change="loadRolesList()" @current-change="loadRolesList" />
</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 { getRolesList, deleteRoles } from '@/addon/zhjw/api/roles'
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 rolesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"name":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const nameList = ref([] as any[])
const nameDictList = async () => {
nameList.value = await (await useDictionary('roles_name')).data.dictionary
}
nameDictList();
/**
* 获取角色管理列表
*/
const loadRolesList = (page: number = 1) => {
rolesTable.loading = true
rolesTable.page = page
getRolesList({
page: rolesTable.page,
limit: rolesTable.limit,
...rolesTable.searchParam
}).then(res => {
rolesTable.loading = false
rolesTable.data = res.data.data
rolesTable.total = res.data.total
}).catch(() => {
rolesTable.loading = false
})
}
loadRolesList()
const router = useRouter()
/**
* 添加角色管理
*/
const addEvent = () => {
router.push('/roles/roles_edit')
}
/**
* 编辑角色管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/roles/roles_edit?id='+data.id)
}
/**
* 删除角色管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('rolesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteRoles(id).then(() => {
loadRolesList()
}).catch(() => {
})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadRolesList()
}
</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>

171
niucloud/addon/zhjw/admin/views/roles/roles_edit.vue

@ -0,0 +1,171 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('name')" prop="name">
<el-radio-group v-model="formData.name" :placeholder="t('namePlaceholder')">
<el-radio
v-for="(item, index) in nameList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('permissions')" prop="permissions">
<el-input v-model="formData.permissions" clearable :placeholder="t('permissionsPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('description')" >
<el-input v-model="formData.description" clearable :placeholder="t('descriptionPlaceholder')" class="input-width" />
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getRolesInfo,addRoles,editRoles } from '@/addon/zhjw/api/roles';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
name: '',
permissions: '',
description: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getRolesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let nameList = ref([])
const nameDictList = async () => {
nameList.value = await (await useDictionary('roles_name')).data.dictionary
}
nameDictList();
watch(() => nameList.value, () => { formData.name = nameList.value[0].value })
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
permissions: [
{ required: true, message: t('permissionsPlaceholder'), trigger: 'blur' },
]
,
description: [
{ required: true, message: t('descriptionPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editRoles : addRoles
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

248
niucloud/addon/zhjw/admin/views/staff/staff.vue

@ -0,0 +1,248 @@
<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('addStaff') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="staffTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="name">
<el-input v-model="staffTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('gender')" prop="gender">
<el-select class="w-[280px]" v-model="staffTable.searchParam.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('phone')" prop="phone">
<el-input v-model="staffTable.searchParam.phone" :placeholder="t('phonePlaceholder')" />
</el-form-item>
<el-form-item :label="t('email')" prop="email">
<el-input v-model="staffTable.searchParam.email" :placeholder="t('emailPlaceholder')" />
</el-form-item>
<el-form-item :label="t('position')" prop="position">
<el-input v-model="staffTable.searchParam.position" :placeholder="t('positionPlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="staffTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('roleId')" prop="role_id">
<el-select class="w-[280px]" v-model="staffTable.searchParam.role_id" clearable :placeholder="t('roleIdPlaceholder')">
<el-option
v-for="(item, index) in roleIdList"
:key="index"
:label="item['description']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadStaffList()">{{ 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="staffTable.data" size="large" v-loading="staffTable.loading">
<template #empty>
<span>{{ !staffTable.loading ? t('emptyData') : '' }}</span>
</template>
<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="phone" :label="t('phone')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="email" :label="t('email')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="position" :label="t('position')" 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 prop="role_id_name" :label="t('roleId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('operation')" fixed="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="staffTable.page" v-model:page-size="staffTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="staffTable.total"
@size-change="loadStaffList()" @current-change="loadStaffList" />
</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 { getStaffList, deleteStaff, getWithRolesList } from '@/addon/zhjw/api/staff'
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 staffTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"name":"",
"gender":"",
"phone":"",
"email":"",
"position":"",
"status":"",
"role_id":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const genderList = ref([] as any[])
const genderDictList = async () => {
genderList.value = await (await useDictionary('sex')).data.dictionary
}
genderDictList();
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('zz_status')).data.dictionary
}
statusDictList();
/**
* 获取人员管理列表
*/
const loadStaffList = (page: number = 1) => {
staffTable.loading = true
staffTable.page = page
getStaffList({
page: staffTable.page,
limit: staffTable.limit,
...staffTable.searchParam
}).then(res => {
staffTable.loading = false
staffTable.data = res.data.data
staffTable.total = res.data.total
}).catch(() => {
staffTable.loading = false
})
}
loadStaffList()
const router = useRouter()
/**
* 添加人员管理
*/
const addEvent = () => {
router.push('/staff/staff_edit')
}
/**
* 编辑人员管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/staff/staff_edit?id='+data.id)
}
/**
* 删除人员管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('staffDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteStaff(id).then(() => {
loadStaffList()
}).catch(() => {
})
})
}
const roleIdList = ref([])
const setRoleIdList = async () => {
roleIdList.value = await (await getWithRolesList({})).data
}
setRoleIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadStaffList()
}
</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>

246
niucloud/addon/zhjw/admin/views/staff/staff_edit.vue

@ -0,0 +1,246 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<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('header')">
<upload-image v-model="formData.header" />
</el-form-item>
<el-form-item :label="t('gender')" prop="gender">
<el-radio-group v-model="formData.gender" :placeholder="t('genderPlaceholder')">
<el-radio
v-for="(item, index) in genderList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('phone')" prop="phone">
<el-input v-model="formData.phone" clearable :placeholder="t('phonePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('email')" prop="email">
<el-input v-model="formData.email" clearable :placeholder="t('emailPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('position')" prop="position">
<el-input v-model="formData.position" clearable :placeholder="t('positionPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('roleId')" prop="role_id">
<el-radio-group v-model="formData.role_id" :placeholder="t('roleIdPlaceholder')">
<el-radio
v-for="(item, index) in roleIdList"
:key="index"
:label="item['id']">
{{ item['description'] }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getStaffInfo,addStaff,editStaff, getWithRolesList } from '@/addon/zhjw/api/staff';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
name: '',
header: '',
gender: '',
phone: '',
email: '',
position: '',
status: '',
role_id: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getStaffInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let genderList = ref([])
const genderDictList = async () => {
genderList.value = await (await useDictionary('sex')).data.dictionary
}
genderDictList();
watch(() => genderList.value, () => { formData.gender = genderList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('zz_status')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const roleIdList = ref([] as any[])
const setRoleIdList = async () => {
roleIdList.value = await (await getWithRolesList({})).data
}
setRoleIdList()
//
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
header: [
{ required: true, message: t('headerPlaceholder'), trigger: 'blur' },
]
,
gender: [
{ required: true, message: t('genderPlaceholder'), trigger: 'blur' },
]
,
phone: [
{ required: true, message: t('phonePlaceholder'), trigger: 'blur' },
]
,
email: [
{ required: true, message: t('emailPlaceholder'), trigger: 'blur' },
]
,
position: [
{ required: true, message: t('positionPlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
role_id: [
{ required: true, message: t('roleIdPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editStaff : addStaff
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

187
niucloud/addon/zhjw/admin/views/timetables/components/timetables-edit.vue

@ -0,0 +1,187 @@
<template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updateTimetables') : t('addTimetables')" 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('classId')" prop="class_id">
<el-radio-group v-model="formData.class_id" :placeholder="t('classIdPlaceholder')">
<el-radio
v-for="(item, index) in classIdList"
:key="index"
:label="item['id']">
{{ item['name'] }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('scheduleJson')" prop="schedule_json">
<el-input v-model="formData.schedule_json" clearable :placeholder="t('scheduleJsonPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('cycle')" prop="cycle">
<el-radio-group v-model="formData.cycle" :placeholder="t('cyclePlaceholder')">
<el-radio
v-for="(item, index) in cycleList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
t('confirm')
}}</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { addTimetables, editTimetables, getTimetablesInfo, getWithClassesList } from '@/addon/zhjw/api/timetables'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
class_id: '',
schedule_json: '',
cycle: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
class_id: [
{ required: true, message: t('classIdPlaceholder'), trigger: 'blur' },
]
,
schedule_json: [
{ required: true, message: t('scheduleJsonPlaceholder'), trigger: 'blur' },
]
,
cycle: [
{ required: true, message: t('cyclePlaceholder'), trigger: 'blur' },
]
,
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editTimetables : addTimetables
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 cycleList = ref([])
const cycleDictList = async () => {
cycleList.value = await (await useDictionary('cycle')).data.dictionary
}
cycleDictList();
watch(() => cycleList.value, () => { formData.cycle = cycleList.value[0].value })
const classIdList = ref([] as any[])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassesList({})).data
}
setClassIdList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if(row){
const data = await (await getTimetablesInfo(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>

197
niucloud/addon/zhjw/admin/views/timetables/timetables.vue

@ -0,0 +1,197 @@
<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('addTimetables') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="timetablesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('classId')" prop="class_id">
<el-select class="w-[280px]" v-model="timetablesTable.searchParam.class_id" clearable :placeholder="t('classIdPlaceholder')">
<el-option
v-for="(item, index) in classIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('cycle')" prop="cycle">
<el-select class="w-[280px]" v-model="timetablesTable.searchParam.cycle" clearable :placeholder="t('cyclePlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in cycleList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadTimetablesList()">{{ 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="timetablesTable.data" size="large" v-loading="timetablesTable.loading">
<template #empty>
<span>{{ !timetablesTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="class_id_name" :label="t('classId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('cycle')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in cycleList">
<div v-if="item.value == row.cycle">{{ 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="timetablesTable.page" v-model:page-size="timetablesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="timetablesTable.total"
@size-change="loadTimetablesList()" @current-change="loadTimetablesList" />
</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 { getTimetablesList, deleteTimetables, getWithClassesList } from '@/addon/zhjw/api/timetables'
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 timetablesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"class_id":"",
"cycle":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const cycleList = ref([] as any[])
const cycleDictList = async () => {
cycleList.value = await (await useDictionary('cycle')).data.dictionary
}
cycleDictList();
/**
* 获取课表管理列表
*/
const loadTimetablesList = (page: number = 1) => {
timetablesTable.loading = true
timetablesTable.page = page
getTimetablesList({
page: timetablesTable.page,
limit: timetablesTable.limit,
...timetablesTable.searchParam
}).then(res => {
timetablesTable.loading = false
timetablesTable.data = res.data.data
timetablesTable.total = res.data.total
}).catch(() => {
timetablesTable.loading = false
})
}
loadTimetablesList()
const router = useRouter()
/**
* 添加课表管理
*/
const addEvent = () => {
router.push('/timetables/timetables_edit')
}
/**
* 编辑课表管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/timetables/timetables_edit?id='+data.id)
}
/**
* 删除课表管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('timetablesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteTimetables(id).then(() => {
loadTimetablesList()
}).catch(() => {
})
})
}
const classIdList = ref([])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassesList({})).data
}
setClassIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadTimetablesList()
}
</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>

184
niucloud/addon/zhjw/admin/views/timetables/timetables_edit.vue

@ -0,0 +1,184 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('classId')" prop="class_id">
<el-radio-group v-model="formData.class_id" :placeholder="t('classIdPlaceholder')">
<el-radio
v-for="(item, index) in classIdList"
:key="index"
:label="item['id']">
{{ item['name'] }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('scheduleJson')" prop="schedule_json">
<el-input v-model="formData.schedule_json" clearable :placeholder="t('scheduleJsonPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('cycle')" prop="cycle">
<el-radio-group v-model="formData.cycle" :placeholder="t('cyclePlaceholder')">
<el-radio
v-for="(item, index) in cycleList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getTimetablesInfo,addTimetables,editTimetables, getWithClassesList } from '@/addon/zhjw/api/timetables';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
class_id: '',
schedule_json: '',
cycle: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getTimetablesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let cycleList = ref([])
const cycleDictList = async () => {
cycleList.value = await (await useDictionary('cycle')).data.dictionary
}
cycleDictList();
watch(() => cycleList.value, () => { formData.cycle = cycleList.value[0].value })
const classIdList = ref([] as any[])
const setClassIdList = async () => {
classIdList.value = await (await getWithClassesList({})).data
}
setClassIdList()
//
const formRules = computed(() => {
return {
class_id: [
{ required: true, message: t('classIdPlaceholder'), trigger: 'blur' },
]
,
schedule_json: [
{ required: true, message: t('scheduleJsonPlaceholder'), trigger: 'blur' },
]
,
cycle: [
{ required: true, message: t('cyclePlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editTimetables : addTimetables
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

242
niucloud/addon/zhjw/admin/views/venues/venues.vue

@ -0,0 +1,242 @@
<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('addVenues') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="venuesTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('campusId')" prop="campus_id">
<el-select class="w-[280px]" v-model="venuesTable.searchParam.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
<el-option
v-for="(item, index) in campusIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('name')" prop="name">
<el-input v-model="venuesTable.searchParam.name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-select class="w-[280px]" v-model="venuesTable.searchParam.type" clearable :placeholder="t('typePlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('capacity')" prop="capacity">
<el-input v-model="venuesTable.searchParam.capacity" :placeholder="t('capacityPlaceholder')" />
</el-form-item>
<el-form-item :label="t('availableTime')" prop="available_time">
<el-input v-model="venuesTable.searchParam.available_time" :placeholder="t('availableTimePlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="venuesTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadVenuesList()">{{ 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="venuesTable.data" size="large" v-loading="venuesTable.loading">
<template #empty>
<span>{{ !venuesTable.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="name" :label="t('name')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('type')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in typeList">
<div v-if="item.value == row.type">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column prop="capacity" :label="t('capacity')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="available_time" :label="t('availableTime')" 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="venuesTable.page" v-model:page-size="venuesTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="venuesTable.total"
@size-change="loadVenuesList()" @current-change="loadVenuesList" />
</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 { getVenuesList, deleteVenues, getWithCampusesList } from '@/addon/zhjw/api/venues'
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 venuesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"campus_id":"",
"name":"",
"type":"",
"capacity":"",
"available_time":"",
"status":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const typeList = ref([] as any[])
const typeDictList = async () => {
typeList.value = await (await useDictionary('cd_type')).data.dictionary
}
typeDictList();
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('cd_status')).data.dictionary
}
statusDictList();
/**
* 获取场地管理列表
*/
const loadVenuesList = (page: number = 1) => {
venuesTable.loading = true
venuesTable.page = page
getVenuesList({
page: venuesTable.page,
limit: venuesTable.limit,
...venuesTable.searchParam
}).then(res => {
venuesTable.loading = false
venuesTable.data = res.data.data
venuesTable.total = res.data.total
}).catch(() => {
venuesTable.loading = false
})
}
loadVenuesList()
const router = useRouter()
/**
* 添加场地管理
*/
const addEvent = () => {
router.push('/venues/venues_edit')
}
/**
* 编辑场地管理
* @param data
*/
const editEvent = (data: any) => {
router.push('/venues/venues_edit?id='+data.id)
}
/**
* 删除场地管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('venuesDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteVenues(id).then(() => {
loadVenuesList()
}).catch(() => {
})
})
}
const campusIdList = ref([])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusesList({})).data
}
setCampusIdList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadVenuesList()
}
</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>

236
niucloud/addon/zhjw/admin/views/venues/venues_edit.vue

@ -0,0 +1,236 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="back()">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
</div>
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('campusId')" prop="campus_id">
<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['name']"
:value="item['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('thumbnail')">
<upload-image v-model="formData.thumbnail" />
</el-form-item>
<el-form-item :label="t('type')" prop="type">
<el-radio-group v-model="formData.type" :placeholder="t('typePlaceholder')">
<el-radio
v-for="(item, index) in typeList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('capacity')" prop="capacity">
<el-input v-model="formData.capacity" clearable :placeholder="t('capacityPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('availableTime')" prop="available_time">
<el-input v-model="formData.available_time" clearable :placeholder="t('availableTimePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-radio-group v-model="formData.status" :placeholder="t('statusPlaceholder')">
<el-radio
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import type { FormInstance } from 'element-plus'
import { getVenuesInfo,addVenues,editVenues, getWithCampusesList } from '@/addon/zhjw/api/venues';
import { useRoute } from 'vue-router'
const route = useRoute()
const id:number = parseInt(route.query.id);
const loading = ref(false)
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData = {
id: 0,
campus_id: '',
name: '',
thumbnail: '',
type: '',
capacity: 0,
available_time: '',
status: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id:number = 0) => {
Object.assign(formData, initialFormData)
const data = await (await getVenuesInfo(id)).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
if(id) setFormData(id);
const formRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
let typeList = ref([])
const typeDictList = async () => {
typeList.value = await (await useDictionary('cd_type')).data.dictionary
}
typeDictList();
watch(() => typeList.value, () => { formData.type = typeList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('cd_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 getWithCampusesList({})).data
}
setCampusIdList()
//
const formRules = computed(() => {
return {
campus_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
]
,
name: [
{ required: true, message: t('namePlaceholder'), trigger: 'blur' },
]
,
thumbnail: [
{ required: true, message: t('thumbnailPlaceholder'), trigger: 'blur' },
]
,
type: [
{ required: true, message: t('typePlaceholder'), trigger: 'blur' },
]
,
capacity: [
{ required: true, message: t('capacityPlaceholder'), trigger: 'blur' },
]
,
available_time: [
{ required: true, message: t('availableTimePlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
}
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
const save = id ? editVenues : addVenues
save(data).then(res => {
loading.value = false
history.back()
}).catch(err => {
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()
}
}
const back = () => {
history.back()
}
</script>
<style lang="scss" scoped></style>

104
niucloud/addon/zhjw/app/adminapi/controller/campuses/Campuses.php

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

104
niucloud/addon/zhjw/app/adminapi/controller/classes/Classes.php

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

107
niucloud/addon/zhjw/app/adminapi/controller/courses/Courses.php

@ -0,0 +1,107 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\adminapi\controller\courses;
use core\base\BaseAdminController;
use addon\zhjw\app\service\admin\courses\CoursesService;
/**
* 课程管理控制器
* Class Courses
* @package addon\zhjw\app\adminapi\controller\courses
*/
class Courses extends BaseAdminController
{
/**
* 获取课程管理列表
* @return \think\Response
*/
public function lists(){
$data = $this->request->params([
["name",""],
["description",""],
["target_group",""],
["taste_price",""],
["price",""],
["is_automatic_signing",""],
["status",""]
]);
return success((new CoursesService())->getPage($data));
}
/**
* 课程管理详情
* @param int $id
* @return \think\Response
*/
public function info(int $id){
return success((new CoursesService())->getInfo($id));
}
/**
* 添加课程管理
* @return \think\Response
*/
public function add(){
$data = $this->request->params([
["name",""],
["description",""],
["thumbnail",""],
["target_group",""],
["duration",""],
["taste_price",0.00],
["price",0.00],
["is_automatic_signing",""],
["automatic_signing_time",0],
["status",""]
]);
$this->validate($data, 'addon\zhjw\app\validate\courses\Courses.add');
$id = (new CoursesService())->add($data);
return success('ADD_SUCCESS', ['id' => $id]);
}
/**
* 课程管理编辑
* @param $id 课程管理id
* @return \think\Response
*/
public function edit(int $id){
$data = $this->request->params([
["name",""],
["description",""],
["thumbnail",""],
["target_group",""],
["duration",""],
["taste_price",0.00],
["price",0.00],
["is_automatic_signing",""],
["automatic_signing_time",0],
["status",""]
]);
$this->validate($data, 'addon\zhjw\app\validate\courses\Courses.edit');
(new CoursesService())->edit($id, $data);
return success('EDIT_SUCCESS');
}
/**
* 课程管理删除
* @param $id 课程管理id
* @return \think\Response
*/
public function del(int $id){
(new CoursesService())->del($id);
return success('DELETE_SUCCESS');
}
}

87
niucloud/addon/zhjw/app/adminapi/controller/roles/Roles.php

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

107
niucloud/addon/zhjw/app/adminapi/controller/staff/Staff.php

@ -0,0 +1,107 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\adminapi\controller\staff;
use core\base\BaseAdminController;
use addon\zhjw\app\service\admin\staff\StaffService;
/**
* 人员管理控制器
* Class Staff
* @package addon\zhjw\app\adminapi\controller\staff
*/
class Staff extends BaseAdminController
{
/**
* 获取人员管理列表
* @return \think\Response
*/
public function lists(){
$data = $this->request->params([
["name",""],
["gender",""],
["phone",""],
["email",""],
["position",""],
["status",""],
["role_id",""]
]);
return success((new StaffService())->getPage($data));
}
/**
* 人员管理详情
* @param int $id
* @return \think\Response
*/
public function info(int $id){
return success((new StaffService())->getInfo($id));
}
/**
* 添加人员管理
* @return \think\Response
*/
public function add(){
$data = $this->request->params([
["name",""],
["header",""],
["gender",""],
["phone",""],
["email",""],
["position",""],
["status",""],
["role_id",0]
]);
$this->validate($data, 'addon\zhjw\app\validate\staff\Staff.add');
$id = (new StaffService())->add($data);
return success('ADD_SUCCESS', ['id' => $id]);
}
/**
* 人员管理编辑
* @param $id 人员管理id
* @return \think\Response
*/
public function edit(int $id){
$data = $this->request->params([
["name",""],
["header",""],
["gender",""],
["phone",""],
["email",""],
["position",""],
["status",""],
["role_id",0]
]);
$this->validate($data, 'addon\zhjw\app\validate\staff\Staff.edit');
(new StaffService())->edit($id, $data);
return success('EDIT_SUCCESS');
}
/**
* 人员管理删除
* @param $id 人员管理id
* @return \think\Response
*/
public function del(int $id){
(new StaffService())->del($id);
return success('DELETE_SUCCESS');
}
public function getRolesAll(){
return success(( new StaffService())->getRolesAll());
}
}

92
niucloud/addon/zhjw/app/adminapi/controller/timetables/Timetables.php

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

104
niucloud/addon/zhjw/app/adminapi/controller/venues/Venues.php

@ -0,0 +1,104 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\adminapi\controller\venues;
use core\base\BaseAdminController;
use addon\zhjw\app\service\admin\venues\VenuesService;
/**
* 场地管理控制器
* Class Venues
* @package addon\zhjw\app\adminapi\controller\venues
*/
class Venues extends BaseAdminController
{
/**
* 获取场地管理列表
* @return \think\Response
*/
public function lists(){
$data = $this->request->params([
["campus_id",""],
["name",""],
["type",""],
["capacity",""],
["available_time",""],
["status",""]
]);
return success((new VenuesService())->getPage($data));
}
/**
* 场地管理详情
* @param int $id
* @return \think\Response
*/
public function info(int $id){
return success((new VenuesService())->getInfo($id));
}
/**
* 添加场地管理
* @return \think\Response
*/
public function add(){
$data = $this->request->params([
["campus_id",0],
["name",""],
["thumbnail",""],
["type",""],
["capacity",0],
["available_time",""],
["status",""]
]);
$this->validate($data, 'addon\zhjw\app\validate\venues\Venues.add');
$id = (new VenuesService())->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],
["name",""],
["thumbnail",""],
["type",""],
["capacity",0],
["available_time",""],
["status",""]
]);
$this->validate($data, 'addon\zhjw\app\validate\venues\Venues.edit');
(new VenuesService())->edit($id, $data);
return success('EDIT_SUCCESS');
}
/**
* 场地管理删除
* @param $id 场地管理id
* @return \think\Response
*/
public function del(int $id){
(new VenuesService())->del($id);
return success('DELETE_SUCCESS');
}
public function getCampusesAll(){
return success(( new VenuesService())->getCampusesAll());
}
}

177
niucloud/addon/zhjw/app/adminapi/route/route.php

@ -27,4 +27,179 @@ Route::group('zhjw', function () {
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,
AdminLog::class AdminLog::class
]); ]);
// USER_CODE_BEGIN -- venues
Route::group('zhjw', function () {
//场地管理列表
Route::get('venues', 'addon\zhjw\app\adminapi\controller\venues\Venues@lists');
//场地管理详情
Route::get('venues/:id', 'addon\zhjw\app\adminapi\controller\venues\Venues@info');
//添加场地管理
Route::post('venues', 'addon\zhjw\app\adminapi\controller\venues\Venues@add');
//编辑场地管理
Route::put('venues/:id', 'addon\zhjw\app\adminapi\controller\venues\Venues@edit');
//删除场地管理
Route::delete('venues/:id', 'addon\zhjw\app\adminapi\controller\venues\Venues@del');
Route::get('campuses_all','addon\zhjw\app\adminapi\controller\venues\Venues@getCampusesAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- venues
// USER_CODE_BEGIN -- campuses
Route::group('zhjw', function () {
//校区管理列表
Route::get('campuses', 'addon\zhjw\app\adminapi\controller\campuses\Campuses@lists');
//校区管理详情
Route::get('campuses/:id', 'addon\zhjw\app\adminapi\controller\campuses\Campuses@info');
//添加校区管理
Route::post('campuses', 'addon\zhjw\app\adminapi\controller\campuses\Campuses@add');
//编辑校区管理
Route::put('campuses/:id', 'addon\zhjw\app\adminapi\controller\campuses\Campuses@edit');
//删除校区管理
Route::delete('campuses/:id', 'addon\zhjw\app\adminapi\controller\campuses\Campuses@del');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- campuses
// USER_CODE_BEGIN -- roles
Route::group('zhjw', function () {
//角色管理列表
Route::get('roles', 'addon\zhjw\app\adminapi\controller\roles\Roles@lists');
//角色管理详情
Route::get('roles/:id', 'addon\zhjw\app\adminapi\controller\roles\Roles@info');
//添加角色管理
Route::post('roles', 'addon\zhjw\app\adminapi\controller\roles\Roles@add');
//编辑角色管理
Route::put('roles/:id', 'addon\zhjw\app\adminapi\controller\roles\Roles@edit');
//删除角色管理
Route::delete('roles/:id', 'addon\zhjw\app\adminapi\controller\roles\Roles@del');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- roles
// USER_CODE_BEGIN -- staff
Route::group('zhjw', function () {
//人员管理列表
Route::get('staff', 'addon\zhjw\app\adminapi\controller\staff\Staff@lists');
//人员管理详情
Route::get('staff/:id', 'addon\zhjw\app\adminapi\controller\staff\Staff@info');
//添加人员管理
Route::post('staff', 'addon\zhjw\app\adminapi\controller\staff\Staff@add');
//编辑人员管理
Route::put('staff/:id', 'addon\zhjw\app\adminapi\controller\staff\Staff@edit');
//删除人员管理
Route::delete('staff/:id', 'addon\zhjw\app\adminapi\controller\staff\Staff@del');
Route::get('roles_all','addon\zhjw\app\adminapi\controller\staff\Staff@getRolesAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- staff
// USER_CODE_BEGIN -- classes
Route::group('zhjw', function () {
//班级管理列表
Route::get('classes', 'addon\zhjw\app\adminapi\controller\classes\Classes@lists');
//班级管理详情
Route::get('classes/:id', 'addon\zhjw\app\adminapi\controller\classes\Classes@info');
//添加班级管理
Route::post('classes', 'addon\zhjw\app\adminapi\controller\classes\Classes@add');
//编辑班级管理
Route::put('classes/:id', 'addon\zhjw\app\adminapi\controller\classes\Classes@edit');
//删除班级管理
Route::delete('classes/:id', 'addon\zhjw\app\adminapi\controller\classes\Classes@del');
Route::get('venues_all','addon\zhjw\app\adminapi\controller\classes\Classes@getVenuesAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- classes
// USER_CODE_BEGIN -- timetables
Route::group('zhjw', function () {
//课表管理列表
Route::get('timetables', 'addon\zhjw\app\adminapi\controller\timetables\Timetables@lists');
//课表管理详情
Route::get('timetables/:id', 'addon\zhjw\app\adminapi\controller\timetables\Timetables@info');
//添加课表管理
Route::post('timetables', 'addon\zhjw\app\adminapi\controller\timetables\Timetables@add');
//编辑课表管理
Route::put('timetables/:id', 'addon\zhjw\app\adminapi\controller\timetables\Timetables@edit');
//删除课表管理
Route::delete('timetables/:id', 'addon\zhjw\app\adminapi\controller\timetables\Timetables@del');
Route::get('classes_all','addon\zhjw\app\adminapi\controller\timetables\Timetables@getClassesAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- timetables
// USER_CODE_BEGIN -- courses
Route::group('zhjw', function () {
//课程管理列表
Route::get('courses', 'addon\zhjw\app\adminapi\controller\courses\Courses@lists');
//课程管理详情
Route::get('courses/:id', 'addon\zhjw\app\adminapi\controller\courses\Courses@info');
//添加课程管理
Route::post('courses', 'addon\zhjw\app\adminapi\controller\courses\Courses@add');
//编辑课程管理
Route::put('courses/:id', 'addon\zhjw\app\adminapi\controller\courses\Courses@edit');
//删除课程管理
Route::delete('courses/:id', 'addon\zhjw\app\adminapi\controller\courses\Courses@del');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- courses

130
niucloud/addon/zhjw/app/model/campuses/Campuses.php

@ -0,0 +1,130 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\model\campuses;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
/**
* 校区管理模型
* Class Campuses
* @package addon\zhjw\app\model\campuses
*/
class Campuses extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'campuses';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'is_deleted';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/**
* 搜索器:校区管理校区名称
* @param $value
* @param $data
*/
public function searchNameAttr($query, $value, $data)
{
if ($value) {
$query->where("name", $value);
}
}
/**
* 搜索器:校区管理坐标地址
* @param $value
* @param $data
*/
public function searchCoordinateAttr($query, $value, $data)
{
if ($value) {
$query->where("coordinate", $value);
}
}
/**
* 搜索器:校区管理校区地址
* @param $value
* @param $data
*/
public function searchAddressAttr($query, $value, $data)
{
if ($value) {
$query->where("address", $value);
}
}
/**
* 搜索器:校区管理联系人
* @param $value
* @param $data
*/
public function searchContactPersonAttr($query, $value, $data)
{
if ($value) {
$query->where("contact_person", $value);
}
}
/**
* 搜索器:校区管理联系电话
* @param $value
* @param $data
*/
public function searchContactPhoneAttr($query, $value, $data)
{
if ($value) {
$query->where("contact_phone", $value);
}
}
/**
* 搜索器:校区管理状态
* @param $value
* @param $data
*/
public function searchStatusAttr($query, $value, $data)
{
if ($value) {
$query->where("status", $value);
}
}
}

136
niucloud/addon/zhjw/app/model/classes/Classes.php

@ -0,0 +1,136 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\model\classes;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
use addon\zhjw\app\model\venues\Venues;
/**
* 班级管理模型
* Class Classes
* @package addon\zhjw\app\model\classes
*/
class Classes extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'classes';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'is_deleted';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/**
* 搜索器:班级管理所属场地
* @param $value
* @param $data
*/
public function searchVenueIdAttr($query, $value, $data)
{
if ($value) {
$query->where("venue_id", $value);
}
}
/**
* 搜索器:班级管理班级名称
* @param $value
* @param $data
*/
public function searchNameAttr($query, $value, $data)
{
if ($value) {
$query->where("name", $value);
}
}
/**
* 搜索器:班级管理最大学员数
* @param $value
* @param $data
*/
public function searchMaxStudentsAttr($query, $value, $data)
{
if ($value) {
$query->where("max_students", $value);
}
}
/**
* 搜索器:班级管理开班时间
* @param $value
* @param $data
*/
public function searchStartDateAttr($query, $value, $data)
{
if ($value) {
$query->where("start_date", $value);
}
}
/**
* 搜索器:班级管理结班时间
* @param $value
* @param $data
*/
public function searchEndDateAttr($query, $value, $data)
{
if ($value) {
$query->where("end_date", $value);
}
}
/**
* 搜索器:班级管理状态
* @param $value
* @param $data
*/
public function searchStatusAttr($query, $value, $data)
{
if ($value) {
$query->where("status", $value);
}
}
public function venues(){
return $this->hasOne(Venues::class, 'id', 'venue_id')->joinType('left')->withField('name,id')->bind(['venue_id_name'=>'name']);
}
}

142
niucloud/addon/zhjw/app/model/courses/Courses.php

@ -0,0 +1,142 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\model\courses;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
/**
* 课程管理模型
* Class Courses
* @package addon\zhjw\app\model\courses
*/
class Courses extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'courses';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'is_deleted';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/**
* 搜索器:课程管理课程名称
* @param $value
* @param $data
*/
public function searchNameAttr($query, $value, $data)
{
if ($value) {
$query->where("name", $value);
}
}
/**
* 搜索器:课程管理课程描述
* @param $value
* @param $data
*/
public function searchDescriptionAttr($query, $value, $data)
{
if ($value) {
$query->where("description", $value);
}
}
/**
* 搜索器:课程管理适合人群
* @param $value
* @param $data
*/
public function searchTargetGroupAttr($query, $value, $data)
{
if ($value) {
$query->where("target_group", $value);
}
}
/**
* 搜索器:课程管理体验价格
* @param $value
* @param $data
*/
public function searchTastePriceAttr($query, $value, $data)
{
if ($value) {
$query->where("taste_price", $value);
}
}
/**
* 搜索器:课程管理正式价格
* @param $value
* @param $data
*/
public function searchPriceAttr($query, $value, $data)
{
if ($value) {
$query->where("price", $value);
}
}
/**
* 搜索器:课程管理是否自动签约
* @param $value
* @param $data
*/
public function searchIsAutomaticSigningAttr($query, $value, $data)
{
if ($value) {
$query->where("is_automatic_signing", $value);
}
}
/**
* 搜索器:课程管理状态
* @param $value
* @param $data
*/
public function searchStatusAttr($query, $value, $data)
{
if ($value) {
$query->where("status", $value);
}
}
}

70
niucloud/addon/zhjw/app/model/roles/Roles.php

@ -0,0 +1,70 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\model\roles;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
/**
* 角色管理模型
* Class Roles
* @package addon\zhjw\app\model\roles
*/
class Roles extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'roles';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'is_deleted';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/**
* 搜索器:角色管理角色名称
* @param $value
* @param $data
*/
public function searchNameAttr($query, $value, $data)
{
if ($value) {
$query->where("name", $value);
}
}
}

148
niucloud/addon/zhjw/app/model/staff/Staff.php

@ -0,0 +1,148 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\model\staff;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
use addon\zhjw\app\model\roles\Roles;
/**
* 人员管理模型
* Class Staff
* @package addon\zhjw\app\model\staff
*/
class Staff extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'staff';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'is_deleted';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/**
* 搜索器:人员管理姓名
* @param $value
* @param $data
*/
public function searchNameAttr($query, $value, $data)
{
if ($value) {
$query->where("name", $value);
}
}
/**
* 搜索器:人员管理性别
* @param $value
* @param $data
*/
public function searchGenderAttr($query, $value, $data)
{
if ($value) {
$query->where("gender", $value);
}
}
/**
* 搜索器:人员管理联系方式
* @param $value
* @param $data
*/
public function searchPhoneAttr($query, $value, $data)
{
if ($value) {
$query->where("phone", $value);
}
}
/**
* 搜索器:人员管理邮箱
* @param $value
* @param $data
*/
public function searchEmailAttr($query, $value, $data)
{
if ($value) {
$query->where("email", $value);
}
}
/**
* 搜索器:人员管理职位
* @param $value
* @param $data
*/
public function searchPositionAttr($query, $value, $data)
{
if ($value) {
$query->where("position", $value);
}
}
/**
* 搜索器:人员管理状态
* @param $value
* @param $data
*/
public function searchStatusAttr($query, $value, $data)
{
if ($value) {
$query->where("status", $value);
}
}
/**
* 搜索器:人员管理角色关系
* @param $value
* @param $data
*/
public function searchRoleIdAttr($query, $value, $data)
{
if ($value) {
$query->where("role_id", $value);
}
}
public function roles(){
return $this->hasOne(Roles::class, 'id', 'role_id')->joinType('left')->withField('description,id')->bind(['role_id_name'=>'description']);
}
}

88
niucloud/addon/zhjw/app/model/timetables/Timetables.php

@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\model\timetables;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
use addon\zhjw\app\model\classes\Classes;
/**
* 课表管理模型
* Class Timetables
* @package addon\zhjw\app\model\timetables
*/
class Timetables extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'timetables';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'is_deleted';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/**
* 搜索器:课表管理所属班级
* @param $value
* @param $data
*/
public function searchClassIdAttr($query, $value, $data)
{
if ($value) {
$query->where("class_id", $value);
}
}
/**
* 搜索器:课表管理周期
* @param $value
* @param $data
*/
public function searchCycleAttr($query, $value, $data)
{
if ($value) {
$query->where("cycle", $value);
}
}
public function classes(){
return $this->hasOne(Classes::class, 'id', 'class_id')->joinType('left')->withField('name,id')->bind(['class_id_name'=>'name']);
}
}

136
niucloud/addon/zhjw/app/model/venues/Venues.php

@ -0,0 +1,136 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\model\venues;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
use addon\zhjw\app\model\campuses\Campuses;
/**
* 场地管理模型
* Class Venues
* @package addon\zhjw\app\model\venues
*/
class Venues extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'venues';
/**
* 定义软删除标记字段.
* @var string
*/
protected $deleteTime = 'is_deleted';
/**
* 定义软删除字段的默认值.
* @var int
*/
protected $defaultSoftDelete = 0;
/**
* 搜索器:场地管理所属校区
* @param $value
* @param $data
*/
public function searchCampusIdAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_id", $value);
}
}
/**
* 搜索器:场地管理场地名称
* @param $value
* @param $data
*/
public function searchNameAttr($query, $value, $data)
{
if ($value) {
$query->where("name", $value);
}
}
/**
* 搜索器:场地管理场地类型
* @param $value
* @param $data
*/
public function searchTypeAttr($query, $value, $data)
{
if ($value) {
$query->where("type", $value);
}
}
/**
* 搜索器:场地管理容纳人数
* @param $value
* @param $data
*/
public function searchCapacityAttr($query, $value, $data)
{
if ($value) {
$query->where("capacity", $value);
}
}
/**
* 搜索器:场地管理可用时间段
* @param $value
* @param $data
*/
public function searchAvailableTimeAttr($query, $value, $data)
{
if ($value) {
$query->where("available_time", $value);
}
}
/**
* 搜索器:场地管理状态
* @param $value
* @param $data
*/
public function searchStatusAttr($query, $value, $data)
{
if ($value) {
$query->where("status", $value);
}
}
public function campuses(){
return $this->hasOne(Campuses::class, 'id', 'campus_id')->joinType('left')->withField('name,id')->bind(['campus_id_name'=>'name']);
}
}

100
niucloud/addon/zhjw/app/service/admin/campuses/CampusesService.php

@ -0,0 +1,100 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\service\admin\campuses;
use addon\zhjw\app\model\campuses\Campuses;
use core\base\BaseAdminService;
/**
* 校区管理服务层
* Class CampusesService
* @package addon\zhjw\app\service\admin\campuses
*/
class CampusesService extends BaseAdminService
{
public function __construct()
{
parent::__construct();
$this->model = new Campuses();
}
/**
* 获取校区管理列表
* @param array $where
* @return array
*/
public function getPage(array $where = [])
{
$field = 'id,name,coordinate,address,contact_person,contact_phone,status,thumbnail,description,is_deleted,created_by,created_role,created_time,updated_by,updated_role,updated_time';
$order = 'id desc';
$search_model = $this->model->withSearch(["name","coordinate","address","contact_person","contact_phone","status"], $where)->field($field)->order($order);
$list = $this->pageQuery($search_model);
return $list;
}
/**
* 获取校区管理信息
* @param int $id
* @return array
*/
public function getInfo(int $id)
{
$field = 'id,name,coordinate,address,contact_person,contact_phone,status,thumbnail,description,is_deleted,created_by,created_role,created_time,updated_by,updated_role,updated_time';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray();
$info['status'] = strval($info['status']);
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;
}
}

106
niucloud/addon/zhjw/app/service/admin/classes/ClassesService.php

@ -0,0 +1,106 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\service\admin\classes;
use addon\zhjw\app\model\classes\Classes;
use addon\zhjw\app\model\venues\Venues;
use core\base\BaseAdminService;
/**
* 班级管理服务层
* Class ClassesService
* @package addon\zhjw\app\service\admin\classes
*/
class ClassesService extends BaseAdminService
{
public function __construct()
{
parent::__construct();
$this->model = new Classes();
}
/**
* 获取班级管理列表
* @param array $where
* @return array
*/
public function getPage(array $where = [])
{
$field = 'id,is_deleted,created_by,created_role,created_time,updated_by,updated_role,updated_time,venue_id,thumbnail,name,max_students,start_date,end_date,status';
$order = 'id desc';
$search_model = $this->model->withSearch(["venue_id","name","max_students","start_date","end_date","status"], $where)->with(['venues'])->field($field)->order($order);
$list = $this->pageQuery($search_model);
return $list;
}
/**
* 获取班级管理信息
* @param int $id
* @return array
*/
public function getInfo(int $id)
{
$field = 'id,is_deleted,created_by,created_role,created_time,updated_by,updated_role,updated_time,venue_id,thumbnail,name,max_students,start_date,end_date,status';
$info = $this->model->field($field)->where([['id', "=", $id]])->with(['venues'])->findOrEmpty()->toArray();
$info['status'] = strval($info['status']);
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 getVenuesAll(){
$venuesModel = new Venues();
return $venuesModel->select()->toArray();
}
}

101
niucloud/addon/zhjw/app/service/admin/courses/CoursesService.php

@ -0,0 +1,101 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace addon\zhjw\app\service\admin\courses;
use addon\zhjw\app\model\courses\Courses;
use core\base\BaseAdminService;
/**
* 课程管理服务层
* Class CoursesService
* @package addon\zhjw\app\service\admin\courses
*/
class CoursesService extends BaseAdminService
{
public function __construct()
{
parent::__construct();
$this->model = new Courses();
}
/**
* 获取课程管理列表
* @param array $where
* @return array
*/
public function getPage(array $where = [])
{
$field = 'id,is_deleted,created_by,created_role,created_time,updated_by,updated_role,updated_time,name,description,thumbnail,target_group,duration,taste_price,price,is_automatic_signing,automatic_signing_time,status';
$order = 'id desc';
$search_model = $this->model->withSearch(["name","description","target_group","taste_price","price","is_automatic_signing","status"], $where)->field($field)->order($order);
$list = $this->pageQuery($search_model);
return $list;
}
/**
* 获取课程管理信息
* @param int $id
* @return array
*/
public function getInfo(int $id)
{
$field = 'id,is_deleted,created_by,created_role,created_time,updated_by,updated_role,updated_time,name,description,thumbnail,target_group,duration,taste_price,price,is_automatic_signing,automatic_signing_time,status';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray();
$info['is_automatic_signing'] = strval($info['is_automatic_signing']);
$info['status'] = strval($info['status']);
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;
}
}

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

Loading…
Cancel
Save