于宏哲PHP 1 year ago
parent
commit
da179fb360
  1. 19
      admin/components.d.ts
  2. 4
      admin/src/addon/zhjw/api/classes.ts
  3. 6
      admin/src/addon/zhjw/lang/zh-cn/classes.classes.json
  4. 8
      admin/src/addon/zhjw/lang/zh-cn/classes.classes_edit.json
  5. 25
      admin/src/addon/zhjw/views/classes/classes.vue
  6. 48
      admin/src/addon/zhjw/views/classes/classes_edit.vue
  7. 413
      admin/src/addon/zhjw/views/timetables/timetables_edit.vue
  8. 4
      niucloud/addon/zhjw/admin/api/classes.ts
  9. 6
      niucloud/addon/zhjw/admin/lang/zh-cn/classes.classes.json
  10. 8
      niucloud/addon/zhjw/admin/lang/zh-cn/classes.classes_edit.json
  11. 25
      niucloud/addon/zhjw/admin/views/classes/classes.vue
  12. 48
      niucloud/addon/zhjw/admin/views/classes/classes_edit.vue
  13. 25
      niucloud/addon/zhjw/app/adminapi/controller/classes/Classes.php
  14. 49
      niucloud/addon/zhjw/app/adminapi/route/route.php
  15. 44
      niucloud/addon/zhjw/app/model/classes/Classes.php
  16. 20
      niucloud/addon/zhjw/app/service/admin/classes/ClassesService.php
  17. 52
      niucloud/app/service/api/member/MemberService.php

19
admin/components.d.ts

@ -10,7 +10,6 @@ declare module '@vue/runtime-core' {
Attachment: typeof import('./src/components/upload-attachment/attachment.vue')['default']
DiyLink: typeof import('./src/components/diy-link/index.vue')['default']
Editor: typeof import('./src/components/editor/index.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
@ -18,26 +17,18 @@ declare module '@vue/runtime-core' {
ElButton: typeof import('element-plus/es')['ElButton']
ElCalendar: typeof import('element-plus/es')['ElCalendar']
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']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
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']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
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']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
@ -46,25 +37,18 @@ declare module '@vue/runtime-core' {
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup']
ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRate: typeof import('element-plus/es')['ElRate']
ElResult: typeof import('element-plus/es')['ElResult']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElStatistic: typeof import('element-plus/es')['ElStatistic']
ElStep: typeof import('element-plus/es')['ElStep']
ElSteps: typeof import('element-plus/es')['ElSteps']
@ -75,9 +59,6 @@ declare module '@vue/runtime-core' {
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
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']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']

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

@ -4,6 +4,8 @@ import request from '@/utils/request'
// USER_CODE_BEGIN -- classes
/**
*
@ -53,6 +55,8 @@ export function deleteClasses(id: number) {
export function getWithVenuesList(params: Record<string,any>){
return request.get('zhjw/venues_all', {params})
}export function getWithStudentsList(params: Record<string,any>){
return request.get('zhjw/students_all', {params})
}
// USER_CODE_END -- classes

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

@ -1,14 +1,10 @@
{
"venueId":"所属场地",
"venueId":"所属场地ID",
"venueIdPlaceholder":"全部",
"name":"班级名称",
"namePlaceholder":"请输入班级名称",
"maxStudents":"最大学员数",
"maxStudentsPlaceholder":"请输入最大学员数",
"startDate":"开班时间",
"startDatePlaceholder":"请输入开班时间",
"endDate":"结班时间",
"endDatePlaceholder":"请输入结班时间",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addClasses":"添加班级管理",

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

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

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

@ -26,19 +26,6 @@
<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')">
@ -68,8 +55,6 @@
<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"/>
@ -106,7 +91,7 @@
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 { getClassesList, deleteClasses, getWithVenuesList, getWithStudentsList } from '@/addon/zhjw/api/classes'
import { img } from '@/utils/common'
import { ElMessageBox,FormInstance } from 'element-plus'
import { useRouter } from 'vue-router'
@ -123,9 +108,6 @@ let classesTable = reactive({
searchParam:{
"venue_id":"",
"name":"",
"max_students":"",
"start_date":"",
"end_date":"",
"status":""
}
})
@ -204,6 +186,11 @@ const deleteEvent = (id: number) => {
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
const maxStudentsList = ref([])
const setMaxStudentsList = async () => {
maxStudentsList.value = await (await getWithStudentsList({})).data
}
setMaxStudentsList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return

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

@ -10,16 +10,16 @@
</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
<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['id']">
{{ item['name'] }}
</el-radio>
</el-radio-group>
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('thumbnail')">
@ -30,8 +30,15 @@
<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 :label="t('maxStudents')" prop="max_students">
<el-checkbox-group v-model="formData.max_students" :placeholder="t('maxStudentsPlaceholder')">
<el-checkbox
v-for="(item, index) in maxStudentsList"
:key="index"
:label="String(item['id'])">
{{ item['name'] }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item :label="t('startDate')" prop="start_date" class="input-width">
@ -55,13 +62,15 @@
</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
<el-select class="input-width" v-model="formData.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
@ -80,7 +89,7 @@ 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 { getClassesInfo,addClasses,editClasses, getWithVenuesList, getWithStudentsList } from '@/addon/zhjw/api/classes';
import { useRoute } from 'vue-router'
const route = useRoute()
@ -98,7 +107,7 @@ const initialFormData = {
venue_id: '',
thumbnail: '',
name: '',
max_students: 0,
max_students: [],
start_date: '',
end_date: '',
status: '',
@ -132,6 +141,11 @@ const selectData = ref<any[]>([])
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
const maxStudentsList = ref([] as any[])
const setMaxStudentsList = async () => {
maxStudentsList.value = await (await getWithStudentsList({})).data
}
setMaxStudentsList()
//
const formRules = computed(() => {
return {

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

@ -1,215 +1,220 @@
<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-select class="input-width" v-model="formData.class_id" clearable :placeholder="t('classIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in classIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('coursesId')" prop="courses_id">
<el-select class="input-width" v-model="formData.courses_id" clearable :placeholder="t('coursesIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in coursesIdList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('scheduleDate')" prop="schedule_date">
<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-select class="input-width" v-model="formData.class_id" clearable
:placeholder="t('classIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option v-for="(item, index) in classIdList" :key="index" :label="item['name']"
:value="item['id']" />
</el-select>
</el-form-item>
<el-form-item :label="t('coursesId')" prop="courses_id">
<el-select class="input-width" v-model="formData.courses_id" clearable
:placeholder="t('coursesIdPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option v-for="(item, index) in coursesIdList" :key="index" :label="item['name']"
:value="item['id']" />
</el-select>
</el-form-item>
<el-form-item :label="t('scheduleDate')" prop="schedule_date">
<el-calendar v-model="formData.schedule_date">
</el-calendar>
</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>
</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, getWithCoursesList } 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
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, getWithCoursesList } 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
let formatDate = (date) => {
// 'YYYYMMDD'
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}
/**
* 表单数据
*/
const initialFormData = {
id: 0,
class_id: '',
courses_id: '',
schedule_date: '',
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 coursesIdList = ref([] as any[])
const setCoursesIdList = async () => {
coursesIdList.value = await (await getWithCoursesList({})).data
}
setCoursesIdList()
//
const formRules = computed(() => {
return {
class_id: [
{ required: true, message: t('classIdPlaceholder'), trigger: 'blur' },
]
,
courses_id: [
{ required: true, message: t('coursesIdPlaceholder'), trigger: 'blur' },
]
,
schedule_date: [
{ required: true, message: t('scheduleDatePlaceholder'), 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
data['schedule_date'] = formatDate(data.schedule_date);
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()
}
if (typeof date === 'string') {
date = new Date(date);
}
// date Date
if (!(date instanceof Date) || isNaN(date.getTime())) {
throw new Error('Invalid date');
}
// 'YYYY-MM-DD'
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}
/**
* 表单数据
*/
const initialFormData = {
id: 0,
class_id: '',
courses_id: '',
schedule_date: '',
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 coursesIdList = ref([] as any[])
const setCoursesIdList = async () => {
coursesIdList.value = await (await getWithCoursesList({})).data
}
setCoursesIdList()
//
const formRules = computed(() => {
return {
class_id: [
{ required: true, message: t('classIdPlaceholder'), trigger: 'blur' },
]
,
courses_id: [
{ required: true, message: t('coursesIdPlaceholder'), trigger: 'blur' },
]
,
schedule_date: [
{ required: true, message: t('scheduleDatePlaceholder'), 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
data['schedule_date'] = formatDate(data.schedule_date);
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>

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

@ -4,6 +4,8 @@ import request from '@/utils/request'
// USER_CODE_BEGIN -- classes
/**
*
@ -53,6 +55,8 @@ export function deleteClasses(id: number) {
export function getWithVenuesList(params: Record<string,any>){
return request.get('zhjw/venues_all', {params})
}export function getWithStudentsList(params: Record<string,any>){
return request.get('zhjw/students_all', {params})
}
// USER_CODE_END -- classes

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

@ -1,14 +1,10 @@
{
"venueId":"所属场地",
"venueId":"所属场地ID",
"venueIdPlaceholder":"全部",
"name":"班级名称",
"namePlaceholder":"请输入班级名称",
"maxStudents":"最大学员数",
"maxStudentsPlaceholder":"请输入最大学员数",
"startDate":"开班时间",
"startDatePlaceholder":"请输入开班时间",
"endDate":"结班时间",
"endDatePlaceholder":"请输入结班时间",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addClasses":"添加班级管理",

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

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

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

@ -26,19 +26,6 @@
<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')">
@ -68,8 +55,6 @@
<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"/>
@ -106,7 +91,7 @@
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 { getClassesList, deleteClasses, getWithVenuesList, getWithStudentsList } from '@/addon/zhjw/api/classes'
import { img } from '@/utils/common'
import { ElMessageBox,FormInstance } from 'element-plus'
import { useRouter } from 'vue-router'
@ -123,9 +108,6 @@ let classesTable = reactive({
searchParam:{
"venue_id":"",
"name":"",
"max_students":"",
"start_date":"",
"end_date":"",
"status":""
}
})
@ -204,6 +186,11 @@ const deleteEvent = (id: number) => {
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
const maxStudentsList = ref([])
const setMaxStudentsList = async () => {
maxStudentsList.value = await (await getWithStudentsList({})).data
}
setMaxStudentsList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return

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

@ -10,16 +10,16 @@
</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
<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['id']">
{{ item['name'] }}
</el-radio>
</el-radio-group>
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('thumbnail')">
@ -30,8 +30,15 @@
<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 :label="t('maxStudents')" prop="max_students">
<el-checkbox-group v-model="formData.max_students" :placeholder="t('maxStudentsPlaceholder')">
<el-checkbox
v-for="(item, index) in maxStudentsList"
:key="index"
:label="item['id']">
{{ item['name'] }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item :label="t('startDate')" prop="start_date" class="input-width">
@ -55,13 +62,15 @@
</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
<el-select class="input-width" v-model="formData.status" clearable :placeholder="t('statusPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
@ -80,7 +89,7 @@ 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 { getClassesInfo,addClasses,editClasses, getWithVenuesList, getWithStudentsList } from '@/addon/zhjw/api/classes';
import { useRoute } from 'vue-router'
const route = useRoute()
@ -98,7 +107,7 @@ const initialFormData = {
venue_id: '',
thumbnail: '',
name: '',
max_students: 0,
max_students: '',
start_date: '',
end_date: '',
status: '',
@ -132,6 +141,11 @@ const selectData = ref<any[]>([])
venueIdList.value = await (await getWithVenuesList({})).data
}
setVenueIdList()
const maxStudentsList = ref([] as any[])
const setMaxStudentsList = async () => {
maxStudentsList.value = await (await getWithStudentsList({})).data
}
setMaxStudentsList()
//
const formRules = computed(() => {
return {

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

@ -30,9 +30,6 @@ class Classes extends BaseAdminController
$data = $this->request->params([
["venue_id",""],
["name",""],
["max_students",""],
["start_date",""],
["end_date",""],
["status",""]
]);
return success((new ClassesService())->getPage($data));
@ -56,10 +53,11 @@ class Classes extends BaseAdminController
["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",""]
["max_students",[]],
["start_date","2025-03-19 10:32:31"],
["end_date","2025-03-19 10:32:31"],
["status",""],
]);
$this->validate($data, 'addon\zhjw\app\validate\classes\Classes.add');
$id = (new ClassesService())->add($data);
@ -76,10 +74,11 @@ class Classes extends BaseAdminController
["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",""]
["max_students",[]],
["start_date","2025-03-19 10:32:31"],
["end_date","2025-03-19 10:32:31"],
["status",""],
]);
$this->validate($data, 'addon\zhjw\app\validate\classes\Classes.edit');
(new ClassesService())->edit($id, $data);
@ -101,4 +100,8 @@ class Classes extends BaseAdminController
return success(( new ClassesService())->getVenuesAll());
}
public function getStudentsAll(){
return success(( new ClassesService())->getStudentsAll());
}
}

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

@ -149,29 +149,6 @@ Route::group('zhjw', function () {
// 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 -- courses
@ -447,3 +424,29 @@ Route::group('zhjw', function () {
AdminLog::class
]);
// USER_CODE_END -- zhjw_follow_up_reminders
// 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');
Route::get('students_all','addon\zhjw\app\adminapi\controller\classes\Classes@getStudentsAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- classes

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

@ -18,6 +18,8 @@ use think\model\relation\HasOne;
use addon\zhjw\app\model\venues\Venues;
use addon\zhjw\app\model\students\Students;
/**
* 班级管理模型
* Class Classes
@ -53,7 +55,7 @@ class Classes extends BaseModel
protected $defaultSoftDelete = 0;
/**
* 搜索器:班级管理所属场地
* 搜索器:班级管理所属场地ID
* @param $value
* @param $data
*/
@ -76,42 +78,6 @@ class Classes extends BaseModel
}
}
/**
* 搜索器:班级管理最大学员数
* @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
@ -133,4 +99,8 @@ class Classes extends BaseModel
return $this->hasOne(Venues::class, 'id', 'venue_id')->joinType('left')->withField('name,id')->bind(['venue_id_name'=>'name']);
}
public function students(){
return $this->hasOne(Students::class, 'id', 'max_students')->joinType('left')->withField('name,id')->bind(['max_students_name'=>'name']);
}
}

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

@ -13,6 +13,7 @@ namespace addon\zhjw\app\service\admin\classes;
use addon\zhjw\app\model\classes\Classes;
use addon\zhjw\app\model\venues\Venues;
use addon\zhjw\app\model\students\Students;
use core\base\BaseAdminService;
@ -37,10 +38,10 @@ class ClassesService extends BaseAdminService
*/
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';
$field = 'id,created_by,created_role,created_time,updated_by,updated_role,updated_time,venue_id,thumbnail,name,max_students,start_date,end_date,status,is_deleted';
$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);
$search_model = $this->model->withSearch(["venue_id","name","status"], $where)->with(['venues','students'])->field($field)->order($order);
$list = $this->pageQuery($search_model);
return $list;
}
@ -52,10 +53,11 @@ class ClassesService extends BaseAdminService
*/
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';
$field = 'id,created_by,created_role,created_time,updated_by,updated_role,updated_time,venue_id,thumbnail,name,max_students,start_date,end_date,status,is_deleted';
$info = $this->model->field($field)->where([['id', "=", $id]])->with(['venues'])->findOrEmpty()->toArray();
$info['status'] = strval($info['status']);
$info = $this->model->field($field)->where([['id', "=", $id]])->with(['venues','students'])->findOrEmpty()->toArray();
$info['max_students'] = explode(',',$info['max_students']);
return $info;
}
@ -66,6 +68,7 @@ class ClassesService extends BaseAdminService
*/
public function add(array $data)
{
$data['max_students'] = implode(',',$data['max_students']);
$res = $this->model->create($data);
return $res->id;
@ -79,7 +82,7 @@ class ClassesService extends BaseAdminService
*/
public function edit(int $id, array $data)
{
$data['max_students'] = implode(',',$data['max_students']);
$this->model->where([['id', '=', $id]])->update($data);
return true;
}
@ -102,5 +105,10 @@ class ClassesService extends BaseAdminService
return $venuesModel->select()->toArray();
}
public function getStudentsAll(){
$studentsModel = new Students();
return $studentsModel->select()->toArray();
}
}

52
niucloud/app/service/api/member/MemberService.php

@ -13,6 +13,7 @@ namespace app\service\api\member;
use addon\zhjw\app\model\classes\Classes;
use addon\zhjw\app\model\courses\Courses;
use addon\zhjw\app\model\students\Students;
use addon\zhjw\app\model\timetables\Timetables;
use addon\zhjw\app\model\venues\Venues;
use app\dict\sys\AppTypeDict;
@ -22,6 +23,7 @@ use core\base\BaseApiService;
use core\exception\ApiException;
use core\util\Barcode;
use core\util\TokenAuth;
use think\facade\Db;
use think\Model;
/**
@ -65,7 +67,13 @@ class MemberService extends BaseApiService
public function getInfo()
{
$field = 'member_id, username, member_no, mobile, register_channel, nickname, headimg, member_level, member_label, login_ip, login_type, login_time, create_time, last_visit_time, last_consum_time, sex, status, birthday, point, balance, growth, is_member, member_time, is_del, province_id, city_id, district_id, address, location, money, money_get, wx_openid, weapp_openid, commission, commission_get, commission_cash_outing';
return $this->model->where([['member_id', '=', $this->member_id]])->with(['member_level_name_bind'])->field($field)->append(['sex_name'])->findOrEmpty()->toArray();
$info = $this->model->where([['member_id', '=', $this->member_id]])
->with(['member_level_name_bind'])
->field($field)
->append(['sex_name'])
->findOrEmpty()->toArray();
$info['students_id'] = (new Students())->where(['user_id' => $this->member_id])->value('id');
return $info;
}
/**
@ -188,10 +196,52 @@ class MemberService extends BaseApiService
//课程列表
public function course_list($data){
$member_info = $this->getInfo();
$venues = new Venues();
$classes = new Classes();
$timetables = new Timetables();
$courses = new Courses();
$classes_list = $classes
->alias("a")
->join(['school_venues' => 'b'],'a.venue_id = b.id','left')
->join(['school_timetables' => 'c'],'a.id = c.class_id','left')
->join(['school_courses' => 'd'],'c.courses_id = d.id','left')
->where('a.start_date', '<=', $data['schedule_date'])
->where('a.end_date', '>=', $data['schedule_date'])
->whereRaw("FIND_IN_SET(?, a.max_students)", [$member_info['students_id']])
->field("a.id,a.venue_id,a.start_date,a.end_date,a.max_students,a.name,b.name as address,d.name as courses_name,
LENGTH(a.max_students) - LENGTH(REPLACE(a.max_students, ',', '')) + 1 as students_count,d.id as courses_id
")
->group("a.venue_id")
->select()->toArray();
$groupedClassesList = [];
foreach ($classes_list as $item) {
$has_sign = Db::name("zhjw_students_sign")
->alias("a")
->join(['school_member' => 'b'],'a.students_id = b.member_id','left')
->where(['a.courses_id' => $item['courses_id']])
->field("a.id,b.headimg")
->select();
$item['has_sign_count'] = count($has_sign);
$item['has_sign_list'] = $has_sign;
$venueId = $item['venue_id'];
if (!isset($groupedClassesList[$venueId])) {
$groupedClassesList[$venueId] = [];
}
$groupedClassesList[$venueId][] = $item;
}
return $groupedClassesList;
$list = $venues->order("id desc")->select()->toArray();
foreach ($list as $k=>$v){

Loading…
Cancel
Save