Browse Source

临时保存

wangzeyan
王泽彦 11 months ago
parent
commit
562dcec4c9
  1. 2
      admin/.gitignore
  2. 16
      admin/components.d.ts
  3. 59
      admin/src/app/api/class.ts
  4. 66
      admin/src/app/api/classroom.ts
  5. 58
      admin/src/app/lang/zh-cn/class.class.json
  6. 27
      admin/src/app/lang/zh-cn/classroom.classroom.json
  7. 2
      admin/src/app/lang/zh-cn/venue.venue.json
  8. 360
      admin/src/app/views/class/class.vue
  9. 327
      admin/src/app/views/class/components/class-edit.vue
  10. 280
      admin/src/app/views/classroom/classroom.vue
  11. 287
      admin/src/app/views/classroom/components/classroom-edit.vue
  12. 186
      admin/src/app/views/venue/components/venue-edit.vue
  13. 64
      niucloud/app/adminapi/controller/classroom/Classroom.php
  14. 4
      niucloud/app/adminapi/controller/venue/Venue.php
  15. 28
      niucloud/app/adminapi/route/class.php
  16. 43
      niucloud/app/adminapi/route/classroom.php
  17. 24
      niucloud/app/adminapi/route/departments.php
  18. 122
      niucloud/app/model/classroom/Classroom.php
  19. 47
      niucloud/app/service/admin/classroom/ClassroomService.php
  20. 21
      niucloud/app/service/admin/venue/VenueService.php
  21. 22
      niucloud/app/validate/classroom/Classroom.php
  22. 12
      niucloud/app/validate/personnel/Personnel.php

2
admin/.gitignore

@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.env.development
.env.production

16
admin/components.d.ts

@ -15,11 +15,13 @@ declare module '@vue/runtime-core' {
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCol: typeof import('element-plus/es')['ElCol']
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']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
@ -32,18 +34,32 @@ declare module '@vue/runtime-core' {
ElImage: typeof import('element-plus/es')['ElImage']
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
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']
ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElStep: typeof import('element-plus/es')['ElStep']
ElSteps: typeof import('element-plus/es')['ElSteps']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTimeSelect: typeof import('element-plus/es')['ElTimeSelect']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload']
ExportSure: typeof import('./src/components/export-sure/index.vue')['default']
HeatMap: typeof import('./src/components/heat-map/index.vue')['default']

59
admin/src/app/api/class.ts

@ -1,59 +0,0 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- class
/**
*
* @param params
* @returns
*/
export function getClassList(params: Record<string, any>) {
return request.get(`class/class`, { params })
}
/**
*
* @param id id
* @returns
*/
export function getClassInfo(id: number) {
return request.get(`class/class/${id}`)
}
/**
*
* @param params
* @returns
*/
export function addClass(params: Record<string, any>) {
return request.post('class/class', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
}
/**
*
* @param id
* @param params
* @returns
*/
export function editClass(params: Record<string, any>) {
return request.put(`class/class/${params.id}`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
}
/**
*
* @param id
* @returns
*/
export function deleteClass(id: number) {
return request.delete(`class/class/${id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
}
// USER_CODE_END -- class

66
admin/src/app/api/classroom.ts

@ -0,0 +1,66 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- class
/**
*
* @param params
* @returns
*/
export function getClassroomList(params: Record<string, any>) {
return request.get(`classroom/classroom`, { params })
}
/**
*
* @param id id
* @returns
*/
export function getClassroomInfo(id: number) {
return request.get(`classroom/classroom/${id}`)
}
/**
*
* @param params
* @returns
*/
export function addClassroom(params: Record<string, any>) {
return request.post('classroom/classroom', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
}
/**
*
* @param id
* @param params
* @returns
*/
export function editClassroom(params: Record<string, any>) {
return request.put(`classroom/classroom/${params.id}`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
}
/**
*
* @param id
* @returns
*/
export function deleteClassroom(id: number) {
return request.delete(`classroom/classroom/${id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
}
export function getWithCampusList(params: Record<string, any>) {
return request.get('classroom/campus_all', { params })
}
export function getWithPersonnelList(params: Record<string, any>) {
return request.get('classroom/personnel_all', { params })
}
// USER_CODE_END -- class

58
admin/src/app/lang/zh-cn/class.class.json

@ -1,35 +1,27 @@
{
"id": "班级编号",
"idPlaceholder": "请输入班级编号",
"campusId": "校区ID",
"campusIdPlaceholder": "请输入校区ID",
"campusName": "校区名称",
"campusNamePlaceholder": "请输入校区名称",
"className": "班级名称",
"classNamePlaceholder": "请输入班级名称",
"headCoach": "班级主教练",
"headCoachPlaceholder": "请输入班级主教练",
"ageGroup": "班级授课年龄段",
"ageGroupPlaceholder": "请输入班级授课年龄段",
"classType": "班级类型",
"classTypePlaceholder": "请输入班级类型",
"assistantCoach": "班级助教",
"assistantCoachPlaceholder": "请输入班级助教",
"createdAt": "创建时间",
"createdAtPlaceholder": "请输入创建时间",
"updatedAt": "修改时间",
"updatedAtPlaceholder": "请输入修改时间",
"deletedAt": "逻辑删除时间",
"deletedAtPlaceholder": "请输入逻辑删除时间",
"status": "班级状态",
"statusPlaceholder": "请输入班级状态",
"sortOrder": "班级排序",
"sortOrderPlaceholder": "请输入班级排序",
"remarks": "班级备注",
"remarksPlaceholder": "请输入班级备注",
"addClass": "添加班级",
"updateClass": "编辑班级",
"classDeleteTips": "确定要删除该数据吗?",
"startDate": "请选择开始时间",
"endDate": "请选择结束时间"
"campusId":"所属校区",
"campusIdPlaceholder":"全部",
"className":"场地名称",
"classNamePlaceholder":"请输入场地名称",
"headCoach":"主教练",
"headCoachPlaceholder":"全部",
"ageGroup":"授课年龄段",
"ageGroupPlaceholder":"请输入授课年龄段",
"classType":"场地类型",
"classTypePlaceholder":"请输入场地类型",
"assistantCoach":"助教",
"assistantCoachPlaceholder":"全部",
"createdAt":"创建时间",
"createdAtPlaceholder":"请输入创建时间",
"status":"班级状态",
"statusPlaceholder":"请输入班级状态",
"sortOrder":"班级排序",
"sortOrderPlaceholder":"请输入班级排序",
"remarks":"班级备注",
"remarksPlaceholder":"请输入班级备注",
"addClass":"添加场地管理",
"updateClass":"编辑场地管理",
"classDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

27
admin/src/app/lang/zh-cn/classroom.classroom.json

@ -0,0 +1,27 @@
{
"campusId":"所属校区",
"campusIdPlaceholder":"全部",
"className":"班级名称",
"classNamePlaceholder":"请输入班级名称",
"headCoach":"主教练",
"headCoachPlaceholder":"全部",
"ageGroup":"授课年龄段",
"ageGroupPlaceholder":"请输入授课年龄段",
"classType":"班级类型",
"classTypePlaceholder":"请输入班级类型",
"assistantCoach":"助教",
"assistantCoachPlaceholder":"全部",
"createdAt":"创建时间",
"createdAtPlaceholder":"请输入创建时间",
"status":"班级状态",
"statusPlaceholder":"请输入班级状态",
"sortOrder":"班级排序",
"sortOrderPlaceholder":"请输入班级排序",
"remarks":"班级备注",
"remarksPlaceholder":"请输入班级备注",
"addClassroom":"添加场地管理",
"updateClassroom":"编辑场地管理",
"classroomDeleteTips":"确定要删除该数据吗?",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间"
}

2
admin/src/app/lang/zh-cn/venue.venue.json

@ -7,7 +7,7 @@
"capacityPlaceholder":"请输入场地可容纳人数上限",
"availabilityStatus":"场地可用状态",
"availabilityStatusPlaceholder":"请输入场地可用状态",
"timeRangeType":"场地可用时间范围类型",
"timeRangeType":"时间范围类型",
"timeRangeTypePlaceholder":"请输入场地可用时间范围类型",
"fixedTimeRanges":"时间范围",
"fixedTimeRangesPlaceholder":"请输入时间范围",

360
admin/src/app/views/class/class.vue

@ -1,360 +0,0 @@
<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('addClass') }}
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="classTable.searchParam"
ref="searchFormRef"
>
<el-form-item :label="t('campusId')" prop="campus_id">
<el-input
v-model="classTable.searchParam.campus_id"
:placeholder="t('campusIdPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('campusName')" prop="campus_name">
<el-input
v-model="classTable.searchParam.campus_name"
:placeholder="t('campusNamePlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('className')" prop="class_name">
<el-input
v-model="classTable.searchParam.class_name"
:placeholder="t('classNamePlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('headCoach')" prop="head_coach">
<el-input
v-model="classTable.searchParam.head_coach"
:placeholder="t('headCoachPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('ageGroup')" prop="age_group">
<el-input
v-model="classTable.searchParam.age_group"
:placeholder="t('ageGroupPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('classType')" prop="class_type">
<el-input
v-model="classTable.searchParam.class_type"
:placeholder="t('classTypePlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('assistantCoach')" prop="assistant_coach">
<el-input
v-model="classTable.searchParam.assistant_coach"
:placeholder="t('assistantCoachPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('createdAt')" prop="created_at">
<el-input
v-model="classTable.searchParam.created_at"
:placeholder="t('createdAtPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('updatedAt')" prop="updated_at">
<el-input
v-model="classTable.searchParam.updated_at"
:placeholder="t('updatedAtPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('deletedAt')" prop="deleted_at">
<el-input
v-model="classTable.searchParam.deleted_at"
:placeholder="t('deletedAtPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-input
v-model="classTable.searchParam.status"
:placeholder="t('statusPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('sortOrder')" prop="sort_order">
<el-input
v-model="classTable.searchParam.sort_order"
:placeholder="t('sortOrderPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('remarks')" prop="remarks">
<el-input
v-model="classTable.searchParam.remarks"
:placeholder="t('remarksPlaceholder')"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadClassList()">{{
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="classTable.data"
size="large"
v-loading="classTable.loading"
>
<template #empty>
<span>{{ !classTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="campus_id"
:label="t('campusId')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="campus_name"
:label="t('campusName')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="class_name"
:label="t('className')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="head_coach"
:label="t('headCoach')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="age_group"
:label="t('ageGroup')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="class_type"
:label="t('classType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="assistant_coach"
:label="t('assistantCoach')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="created_at"
:label="t('createdAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="updated_at"
:label="t('updatedAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="deleted_at"
:label="t('deletedAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="status"
:label="t('status')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="sort_order"
:label="t('sortOrder')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="remarks"
:label="t('remarks')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="classTable.page"
v-model:page-size="classTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="classTable.total"
@size-change="loadClassList()"
@current-change="loadClassList"
/>
</div>
</div>
<edit ref="editClassDialog" @complete="loadClassList" />
</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 { getClassList, deleteClass } from '@/app/api/class'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import Edit from '@/app/views/class/components/class-edit.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
let classTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
campus_id: '',
campus_name: '',
class_name: '',
head_coach: '',
age_group: '',
class_type: '',
assistant_coach: '',
created_at: '',
updated_at: '',
deleted_at: '',
status: '',
sort_order: '',
remarks: '',
},
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
/**
* 获取班级列表
*/
const loadClassList = (page: number = 1) => {
classTable.loading = true
classTable.page = page
getClassList({
page: classTable.page,
limit: classTable.limit,
...classTable.searchParam,
})
.then((res) => {
classTable.loading = false
classTable.data = res.data.data
classTable.total = res.data.total
})
.catch(() => {
classTable.loading = false
})
}
loadClassList()
const editClassDialog: Record<string, any> | null = ref(null)
/**
* 添加班级
*/
const addEvent = () => {
editClassDialog.value.setFormData()
editClassDialog.value.showDialog = true
}
/**
* 编辑班级
* @param data
*/
const editEvent = (data: any) => {
editClassDialog.value.setFormData(data)
editClassDialog.value.showDialog = true
}
/**
* 删除班级
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('classDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteClass(id)
.then(() => {
loadClassList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadClassList()
}
</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>

327
admin/src/app/views/class/components/class-edit.vue

@ -1,327 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="formData.id ? t('updateClass') : t('addClass')"
width="50%"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('campusId')" prop="campus_id">
<el-input
v-model="formData.campus_id"
clearable
:placeholder="t('campusIdPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('campusName')" prop="campus_name">
<el-input
v-model="formData.campus_name"
clearable
:placeholder="t('campusNamePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('className')" prop="class_name">
<el-input
v-model="formData.class_name"
clearable
:placeholder="t('classNamePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('headCoach')" prop="head_coach">
<el-input
v-model="formData.head_coach"
clearable
:placeholder="t('headCoachPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('ageGroup')" prop="age_group">
<el-input
v-model="formData.age_group"
clearable
:placeholder="t('ageGroupPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('classType')" prop="class_type">
<el-input
v-model="formData.class_type"
clearable
:placeholder="t('classTypePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('assistantCoach')" prop="assistant_coach">
<el-input
v-model="formData.assistant_coach"
clearable
:placeholder="t('assistantCoachPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('createdAt')">
<el-input
v-model="formData.created_at"
clearable
:placeholder="t('createdAtPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('updatedAt')">
<el-input
v-model="formData.updated_at"
clearable
:placeholder="t('updatedAtPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('deletedAt')">
<el-input
v-model="formData.deleted_at"
clearable
:placeholder="t('deletedAtPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-input
v-model="formData.status"
clearable
:placeholder="t('statusPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('sortOrder')" prop="sort_order">
<el-input
v-model="formData.sort_order"
clearable
:placeholder="t('sortOrderPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('remarks')">
<el-input
v-model="formData.remarks"
clearable
:placeholder="t('remarksPlaceholder')"
class="input-width"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template>
<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 { addClass, editClass, getClassInfo } from '@/app/api/class'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
campus_id: '',
campus_name: '',
class_name: '',
head_coach: '',
age_group: '',
class_type: '',
assistant_coach: '',
created_at: '',
updated_at: '',
deleted_at: '',
status: '',
sort_order: '',
remarks: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
campus_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
],
campus_name: [
{ required: true, message: t('campusNamePlaceholder'), trigger: 'blur' },
],
class_name: [
{ required: true, message: t('classNamePlaceholder'), trigger: 'blur' },
],
head_coach: [
{ required: true, message: t('headCoachPlaceholder'), trigger: 'blur' },
],
age_group: [
{ required: true, message: t('ageGroupPlaceholder'), trigger: 'blur' },
],
class_type: [
{ required: true, message: t('classTypePlaceholder'), trigger: 'blur' },
],
assistant_coach: [
{
required: true,
message: t('assistantCoachPlaceholder'),
trigger: 'blur',
},
],
created_at: [
{ required: true, message: t('createdAtPlaceholder'), trigger: 'blur' },
],
updated_at: [
{ required: true, message: t('updatedAtPlaceholder'), trigger: 'blur' },
],
deleted_at: [
{ required: true, message: t('deletedAtPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
sort_order: [
{ required: true, message: t('sortOrderPlaceholder'), trigger: 'blur' },
],
remarks: [
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editClass : addClass
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
}
//
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if (row) {
const data = await (await getClassInfo(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>

280
admin/src/app/views/classroom/classroom.vue

@ -0,0 +1,280 @@
<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('addClassroom') }}
</el-button>
</div>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="classroomTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('campusId')" prop="campus_id">
<el-select class="w-[280px]" v-model="classroomTable.searchParam.campus_id" clearable :placeholder="t('campusIdPlaceholder')">
<el-option
v-for="(item, index) in campusIdList"
:key="index"
:label="item['campus_name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('className')" prop="class_name">
<el-input v-model="classroomTable.searchParam.class_name" :placeholder="t('classNamePlaceholder')" />
</el-form-item>
<el-form-item :label="t('headCoach')" prop="head_coach">
<el-select class="w-[280px]" v-model="classroomTable.searchParam.head_coach" clearable :placeholder="t('headCoachPlaceholder')">
<el-option
v-for="(item, index) in headCoachList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('classType')" prop="class_type">
<el-select class="w-[280px]" v-model="classroomTable.searchParam.class_type" clearable :placeholder="t('classTypePlaceholder')">
<el-option label="全部" value=""></el-option>
<el-option
v-for="(item, index) in class_typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('assistantCoach')" prop="assistant_coach">
<el-select class="w-[280px]" v-model="classroomTable.searchParam.assistant_coach" clearable :placeholder="t('assistantCoachPlaceholder')">
<el-option
v-for="(item, index) in assistantCoachList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('createdAt')" prop="created_at">
<el-input v-model="classroomTable.searchParam.created_at" :placeholder="t('createdAtPlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="w-[280px]" v-model="classroomTable.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="loadClassroomList()">{{ 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="classroomTable.data" size="large" v-loading="classroomTable.loading">
<template #empty>
<span>{{ !classroomTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="campus_id_name" :label="t('campusId')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="class_name" :label="t('className')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="head_coach_name" :label="t('headCoach')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="age_group" :label="t('ageGroup')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column :label="t('classType')" min-width="180" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
<div v-for="(item, index) in class_typeList">
<div v-if="item.value == row.class_type">{{ item.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column prop="assistant_coach_name" :label="t('assistantCoach')" 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="sort_order" :label="t('sortOrder')" 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="classroomTable.page" v-model:page-size="classroomTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="classroomTable.total"
@size-change="loadClassroomList()" @current-change="loadClassroomList" />
</div>
</div>
<edit ref="editClassroomDialog" @complete="loadClassroomList" />
</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 { getClassroomList, deleteClassroom, getWithCampusList, getWithPersonnelList, getWithPersonnelList } from '@/app/api/classroom'
import { img } from '@/utils/common'
import { ElMessageBox,FormInstance } from 'element-plus'
import Edit from '@/app/views/classroom/components/classroom-edit.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title;
let classroomTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam:{
"campus_id":"",
"class_name":"",
"head_coach":"",
"class_type":"",
"assistant_coach":"",
"created_at":"",
"status":""
}
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
const class_typeList = ref([] as any[])
const class_typeDictList = async () => {
class_typeList.value = await (await useDictionary('class_type')).data.dictionary
}
class_typeDictList();
const statusList = ref([] as any[])
const statusDictList = async () => {
statusList.value = await (await useDictionary('SiteStatus')).data.dictionary
}
statusDictList();
/**
* 获取场地管理列表
*/
const loadClassroomList = (page: number = 1) => {
classroomTable.loading = true
classroomTable.page = page
getClassroomList({
page: classroomTable.page,
limit: classroomTable.limit,
...classroomTable.searchParam
}).then(res => {
classroomTable.loading = false
classroomTable.data = res.data.data
classroomTable.total = res.data.total
}).catch(() => {
classroomTable.loading = false
})
}
loadClassroomList()
const editClassroomDialog: Record<string, any> | null = ref(null)
/**
* 添加场地管理
*/
const addEvent = () => {
editClassroomDialog.value.setFormData()
editClassroomDialog.value.showDialog = true
}
/**
* 编辑场地管理
* @param data
*/
const editEvent = (data: any) => {
editClassroomDialog.value.setFormData(data)
editClassroomDialog.value.showDialog = true
}
/**
* 删除场地管理
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('classroomDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}
).then(() => {
deleteClassroom(id).then(() => {
loadClassroomList()
}).catch(() => {
})
})
}
const campusIdList = ref([])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const headCoachList = ref([])
const setHeadCoachList = async () => {
headCoachList.value = await (await getWithPersonnelList({})).data
}
setHeadCoachList()
const assistantCoachList = ref([])
const setAssistantCoachList = async () => {
assistantCoachList.value = await (await getWithPersonnelList({})).data
}
setAssistantCoachList()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadClassroomList()
}
</script>
<style lang="scss" scoped>
/* 多行超出隐藏 */
.multi-hidden {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>

287
admin/src/app/views/classroom/components/classroom-edit.vue

@ -0,0 +1,287 @@
<template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updateClassroom') : t('addClassroom')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('campusId')" 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['campus_name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('className')" prop="class_name">
<el-input v-model="formData.class_name" clearable :placeholder="t('classNamePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('headCoach')" prop="head_coach">
<el-select class="input-width" v-model="formData.head_coach" clearable :placeholder="t('headCoachPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in headCoachList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('ageGroup')" >
<el-input v-model="formData.age_group" clearable :placeholder="t('ageGroupPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('classType')" prop="class_type">
<el-select class="input-width" v-model="formData.class_type" clearable :placeholder="t('classTypePlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in class_typeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="t('assistantCoach')" >
<el-select class="input-width" v-model="formData.assistant_coach" clearable :placeholder="t('assistantCoachPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in assistantCoachList"
:key="index"
:label="item['name']"
:value="item['id']"
/>
</el-select>
</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('sortOrder')" >
<el-input v-model="formData.sort_order" clearable :placeholder="t('sortOrderPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('remarks')" >
<el-input v-model="formData.remarks" clearable :placeholder="t('remarksPlaceholder')" class="input-width" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
t('confirm')
}}</el-button>
</span>
</template>
</el-dialog>
</template>
<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 { addClassroom, editClassroom, getClassroomInfo, getWithCampusList, getWithPersonnelList, getWithPersonnelList } from '@/app/api/classroom'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
campus_id: '',
class_name: '',
head_coach: '',
age_group: '',
class_type: '',
assistant_coach: '',
status: '',
sort_order: '',
remarks: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
campus_id: [
{ required: true, message: t('campusIdPlaceholder'), trigger: 'blur' },
]
,
class_name: [
{ required: true, message: t('classNamePlaceholder'), trigger: 'blur' },
]
,
head_coach: [
{ required: true, message: t('headCoachPlaceholder'), trigger: 'blur' },
]
,
age_group: [
{ required: true, message: t('ageGroupPlaceholder'), trigger: 'blur' },
]
,
class_type: [
{ required: true, message: t('classTypePlaceholder'), trigger: 'blur' },
]
,
assistant_coach: [
{ required: true, message: t('assistantCoachPlaceholder'), trigger: 'blur' },
]
,
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
]
,
sort_order: [
{ required: true, message: t('sortOrderPlaceholder'), trigger: 'blur' },
]
,
remarks: [
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
]
,
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editClassroom : addClassroom
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 class_typeList = ref([])
const class_typeDictList = async () => {
class_typeList.value = await (await useDictionary('class_type')).data.dictionary
}
class_typeDictList();
watch(() => class_typeList.value, () => { formData.class_type = class_typeList.value[0].value })
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (await useDictionary('SiteStatus')).data.dictionary
}
statusDictList();
watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
const campusIdList = ref([] as any[])
const setCampusIdList = async () => {
campusIdList.value = await (await getWithCampusList({})).data
}
setCampusIdList()
const headCoachList = ref([] as any[])
const setHeadCoachList = async () => {
headCoachList.value = await (await getWithPersonnelList({})).data
}
setHeadCoachList()
const assistantCoachList = ref([] as any[])
const setAssistantCoachList = async () => {
assistantCoachList.value = await (await getWithPersonnelList({})).data
}
setAssistantCoachList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
if(row){
const data = await (await getClassroomInfo(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>

186
admin/src/app/views/venue/components/venue-edit.vue

@ -56,15 +56,13 @@
v-model="formData.availability_status"
:placeholder="t('availabilityStatusPlaceholder')"
>
<!-- <el-radio-->
<!-- v-for="(item, index) in availability_statusList"-->
<!-- :key="index"-->
<!-- :label="item.value"-->
<!-- >-->
<!-- {{ item.name }}-->
<!-- </el-radio>-->
<el-radio label="1">可用</el-radio>
<el-radio label="2">不可用</el-radio>
<el-radio
v-for="(item, index) in availability_statusList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
@ -83,16 +81,81 @@
</el-radio-group>
</el-form-item>
<el-form-item :label="t('fixedTimeRanges')" class="input-width">
<el-date-picker
class="flex-1 !flex"
v-model="formData.fixed_time_ranges"
clearable
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('fixedTimeRangesPlaceholder')"
>
</el-date-picker>
<el-form-item
:label="t('fixedTimeRanges')"
class="input-width"
v-if="formData.time_range_type === 'range'"
>
<div class="flex items-center gap-[20px]" style="margin-bottom: 10px">
<div class="flex items-center gap-[10px] w-full">
<el-time-select
class="flex-1"
v-model="formData.time_range_start"
clearable
:start="getStartTime(0)"
:end="getEndTime(0)"
step="00:10"
value-format="HH:mm"
placeholder="开始时间"
/>
<span class="text-gray-500"></span>
<el-time-select
class="flex-1"
v-model="formData.time_range_end"
clearable
:start="formData.time_range_start || '08:30'"
end="22:30"
step="00:10"
value-format="HH:mm"
placeholder="结束时间"
/>
</div>
</div>
</el-form-item>
<el-form-item
:label="t('fixedTimeRanges')"
class="input-width"
v-if="formData.time_range_type === 'fixed'"
>
<div v-for="(time, index) in formData.fixed_time_ranges" :key="index">
<div class="flex items-center gap-[20px]" style="margin-bottom: 10px">
<div class="flex items-center gap-[10px] w-full">
<el-time-select
class="flex-1"
v-model="formData.fixed_time_ranges[index].start_time"
clearable
:start="getStartTime(index)"
:end="getEndTime(index)"
step="00:10"
value-format="HH:mm"
placeholder="开始时间"
/>
<span class="text-gray-500"></span>
<el-time-select
class="flex-1"
v-model="formData.fixed_time_ranges[index].end_time"
clearable
:start="formData.fixed_time_ranges[index].start_time || '08:30'"
end="22:30"
step="00:10"
value-format="HH:mm"
placeholder="结束时间"
/>
</div>
<el-button
v-if="index === formData.fixed_time_ranges.length - 1"
type="primary"
@click="addTimeRange"
>新增</el-button
>
<el-button
v-if="index > 0"
type="danger"
@click="removeTimeRange(index)"
>删除</el-button
>
</div>
</div>
</el-form-item>
</el-form>
@ -112,6 +175,7 @@
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
@ -133,9 +197,14 @@ const initialFormData = {
campus_id: '',
venue_name: '',
capacity: '',
availability_status: '',
availability_status: '1',
time_range_type: '',
fixed_time_ranges: '',
fixed_time_ranges: [
{
start_time: '',
end_time: '',
},
],
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -176,18 +245,78 @@ const formRules = computed(() => {
trigger: 'blur',
},
],
fixed_time_ranges: [
{
required: true,
message: t('fixedTimeRangesPlaceholder'),
trigger: 'blur',
},
],
}
})
const emit = defineEmits(['complete'])
const addTimeRange = () => {
const usedTimeRanges = formData.fixed_time_ranges
.filter((range) => range.start_time && range.end_time)
.map((range) => {
return {
start: range.start_time,
end: range.end_time,
}
})
const newRange = {
start_time: '',
end_time: '',
}
//
if (usedTimeRanges.length > 0) {
const lastRange = usedTimeRanges[usedTimeRanges.length - 1]
newRange.start_time = lastRange.end
newRange.end_time = lastRange.end
} else {
newRange.start_time = '08:30'
newRange.end_time = '08:30'
}
//
const isOverlap = usedTimeRanges.some((range) => {
return (
(newRange.start_time >= range.start && newRange.start_time < range.end) ||
(newRange.end_time > range.start && newRange.end_time <= range.end)
)
})
formData.fixed_time_ranges.push(newRange)
}
//
const getStartTime = (index: number) => {
if (index === 0) return '08:30'
const prevRange = formData.fixed_time_ranges[index - 1]
return prevRange.end_time || '08:30'
}
const getEndTime = (index: number) => {
if (index === formData.fixed_time_ranges.length - 1) return '22:30'
const nextRange = formData.fixed_time_ranges[index + 1]
return nextRange.start_time || '22:30'
}
const removeTimeRange = (index: number) => {
formData.fixed_time_ranges.splice(index, 1)
}
watch(
() => formData.fixed_time_ranges,
(newValue) => {
newValue.forEach((timeRange) => {
if (
timeRange.start_time &&
timeRange.end_time &&
timeRange.start_time > timeRange.end_time
) {
timeRange.end_time = ''
}
})
},
{ deep: true }
)
/**
* 确认
* @param formEl
@ -250,6 +379,7 @@ const setCampusIdList = async () => {
setCampusIdList()
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
formData.fixed_time_ranges = initialFormData.fixed_time_ranges
loading.value = true
if (row) {
const data = await (await getVenueInfo(row.id)).data

64
niucloud/app/adminapi/controller/class/Class.php → niucloud/app/adminapi/controller/classroom/Classroom.php

@ -9,111 +9,105 @@
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\adminapi\controller\class;
namespace app\adminapi\controller\classroom;
use core\base\BaseAdminController;
use app\service\admin\class\ClassService;
use app\service\admin\classroom\ClassroomService;
/**
* 班级控制器
* Class Class
* @package app\adminapi\controller\class
* 场地管理控制器
* Class Classroom
* @package app\adminapi\controller\classroom
*/
class Class extends BaseAdminController
class Classroom extends BaseAdminController
{
/**
* 获取班级列表
* 获取场地管理列表
* @return \think\Response
*/
public function lists(){
$data = $this->request->params([
["campus_id",""],
["campus_name",""],
["class_name",""],
["head_coach",""],
["age_group",""],
["class_type",""],
["assistant_coach",""],
["created_at",""],
["updated_at",""],
["deleted_at",""],
["status",""],
["sort_order",""],
["remarks",""]
["status",""]
]);
return success((new ClassService())->getPage($data));
return success((new ClassroomService())->getPage($data));
}
/**
* 班级详情
* 场地管理详情
* @param int $id
* @return \think\Response
*/
public function info(int $id){
return success((new ClassService())->getInfo($id));
return success((new ClassroomService())->getInfo($id));
}
/**
* 添加班级
* 添加场地管理
* @return \think\Response
*/
public function add(){
$data = $this->request->params([
["campus_id",0],
["campus_name",""],
["class_name",""],
["head_coach",""],
["age_group",""],
["class_type",""],
["assistant_coach",""],
["created_at",1747386439],
["updated_at",1747386439],
["deleted_at",1747386439],
["status",""],
["sort_order",0],
["remarks",""]
]);
$this->validate($data, 'app\validate\class\Class.add');
$id = (new ClassService())->add($data);
$this->validate($data, 'app\validate\classroom\Classroom.add');
$id = (new ClassroomService())->add($data);
return success('ADD_SUCCESS', ['id' => $id]);
}
/**
* 班级编辑
* @param $id 班级id
* 场地管理编辑
* @param $id 场地管理id
* @return \think\Response
*/
public function edit(int $id){
$data = $this->request->params([
["campus_id",0],
["campus_name",""],
["class_name",""],
["head_coach",""],
["age_group",""],
["class_type",""],
["assistant_coach",""],
["created_at",1747386439],
["updated_at",1747386439],
["deleted_at",1747386439],
["status",""],
["sort_order",0],
["remarks",""]
]);
$this->validate($data, 'app\validate\class\Class.edit');
(new ClassService())->edit($id, $data);
$this->validate($data, 'app\validate\classroom\Classroom.edit');
(new ClassroomService())->edit($id, $data);
return success('EDIT_SUCCESS');
}
/**
* 班级删除
* @param $id 班级id
* 场地管理删除
* @param $id 场地管理id
* @return \think\Response
*/
public function del(int $id){
(new ClassService())->del($id);
(new ClassroomService())->del($id);
return success('DELETE_SUCCESS');
}
public function getCampusAll(){
return success(( new ClassroomService())->getCampusAll());
}
public function getPersonnelAll(){
return success(( new ClassroomService())->getPersonnelAll());
}
}

4
niucloud/app/adminapi/controller/venue/Venue.php

@ -59,7 +59,7 @@ class Venue extends BaseAdminController
["capacity",0],
["availability_status",0],
["time_range_type",""],
["fixed_time_ranges",""],
["fixed_time_ranges",[]],
]);
$this->validate($data, 'app\validate\venue\Venue.add');
@ -79,7 +79,7 @@ class Venue extends BaseAdminController
["capacity",0],
["availability_status",0],
["time_range_type",""],
["fixed_time_ranges",""],
["fixed_time_ranges",[]],
]);
$this->validate($data, 'app\validate\venue\Venue.edit');

28
niucloud/app/adminapi/route/class.php

@ -37,3 +37,31 @@ Route::group('campus', function () {
AdminLog::class
]);
// USER_CODE_END -- campus
// USER_CODE_BEGIN -- class
Route::group('class', function () {
//场地管理列表
Route::get('class', 'class.Class/lists');
//场地管理详情
Route::get('class/:id', 'class.Class/info');
//添加场地管理
Route::post('class', 'class.Class/add');
//编辑场地管理
Route::put('class/:id', 'class.Class/edit');
//删除场地管理
Route::delete('class/:id', 'class.Class/del');
Route::get('campus_all','class.Class/getCampusAll');
Route::get('personnel_all','class.Class/getPersonnelAll');
Route::get('personnel_all','class.Class/getPersonnelAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- class

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

@ -0,0 +1,43 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- class
Route::group('classroom', function () {
//场地管理列表
Route::get('classroom', 'classroom.Classroom/lists');
//场地管理详情
Route::get('classroom/:id', 'classroom.Classroom/info');
//添加场地管理
Route::post('classroom', 'classroom.Classroom/add');
//编辑场地管理
Route::put('classroom/:id', 'classroom.Classroom/edit');
//删除场地管理
Route::delete('classroom/:id', 'classroom.Classroom/del');
Route::get('campus_all','classroom.Classroom/getCampusAll');
Route::get('personnel_all','classroom.Classroom/getPersonnelAll');
Route::get('personnel_all','classroom.Classroom/getPersonnelAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- class

24
niucloud/app/adminapi/route/departments.php

@ -38,27 +38,3 @@ Route::group('departments', function () {
AdminLog::class
]);
// USER_CODE_END -- departments
// USER_CODE_BEGIN -- departments
Route::group('departments', function () {
//部门列表
Route::get('departments', 'departments.Departments/lists');
//部门详情
Route::get('departments/:id', 'departments.Departments/info');
//添加部门
Route::post('departments', 'departments.Departments/add');
//编辑部门
Route::put('departments/:id', 'departments.Departments/edit');
//删除部门
Route::delete('departments/:id', 'departments.Departments/del');
Route::get('departments_all','departments.Departments/getDepartmentsAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);
// USER_CODE_END -- departments

122
niucloud/app/model/class/Class.php → niucloud/app/model/classroom/Classroom.php

@ -9,19 +9,25 @@
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\model\class;
namespace app\model\classroom;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
use app\model\personnel\Personnel;
/**
* 班级模型
* Class Class
* @package app\model\class
* 场地管理模型
* Class Classroom
* @package app\model\classroom
*/
class Class extends BaseModel
class Classroom extends BaseModel
{
use SoftDelete;
@ -51,19 +57,7 @@ class Class extends BaseModel
protected $defaultSoftDelete = 0;
/**
* 搜索器:班级班级编号
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:班级校区ID
* 搜索器:场地管理所属校区
* @param $value
* @param $data
*/
@ -75,19 +69,7 @@ class Class extends BaseModel
}
/**
* 搜索器:班级校区名称
* @param $value
* @param $data
*/
public function searchCampusNameAttr($query, $value, $data)
{
if ($value) {
$query->where("campus_name", $value);
}
}
/**
* 搜索器:班级班级名称
* 搜索器:场地管理班级名称
* @param $value
* @param $data
*/
@ -99,7 +81,7 @@ class Class extends BaseModel
}
/**
* 搜索器:班级班级主教练
* 搜索器:场地管理主教练
* @param $value
* @param $data
*/
@ -111,19 +93,7 @@ class Class extends BaseModel
}
/**
* 搜索器:班级班级授课年龄段
* @param $value
* @param $data
*/
public function searchAgeGroupAttr($query, $value, $data)
{
if ($value) {
$query->where("age_group", $value);
}
}
/**
* 搜索器:班级班级类型
* 搜索器:场地管理班级类型
* @param $value
* @param $data
*/
@ -135,7 +105,7 @@ class Class extends BaseModel
}
/**
* 搜索器:班级班级助教
* 搜索器:场地管理助教
* @param $value
* @param $data
*/
@ -147,7 +117,7 @@ class Class extends BaseModel
}
/**
* 搜索器:班级创建时间
* 搜索器:场地管理创建时间
* @param $value
* @param $data
*/
@ -159,31 +129,7 @@ class Class extends BaseModel
}
/**
* 搜索器:班级修改时间
* @param $value
* @param $data
*/
public function searchUpdatedAtAttr($query, $value, $data)
{
if ($value) {
$query->where("updated_at", $value);
}
}
/**
* 搜索器:班级逻辑删除时间
* @param $value
* @param $data
*/
public function searchDeletedAtAttr($query, $value, $data)
{
if ($value) {
$query->where("deleted_at", $value);
}
}
/**
* 搜索器:班级班级状态
* 搜索器:场地管理班级状态
* @param $value
* @param $data
*/
@ -194,33 +140,21 @@ class Class extends BaseModel
}
}
/**
* 搜索器:班级班级排序
* @param $value
* @param $data
*/
public function searchSortOrderAttr($query, $value, $data)
{
if ($value) {
$query->where("sort_order", $value);
}
}
/**
* 搜索器:班级班级备注
* @param $value
* @param $data
*/
public function searchRemarksAttr($query, $value, $data)
{
if ($value) {
$query->where("remarks", $value);
}
}
public function campus(){
return $this->hasOne(Campus::class, 'id', 'campus_id')->joinType('left')->withField('campus_name,id')->bind(['campus_id_name'=>'campus_name']);
}
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'head_coach')->joinType('left')->withField('name,id')->bind(['head_coach_name'=>'name']);
}
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'assistant_coach')->joinType('left')->withField('name,id')->bind(['assistant_coach_name'=>'name']);
}
}

47
niucloud/app/service/admin/class/ClassService.php → niucloud/app/service/admin/classroom/ClassroomService.php

@ -9,28 +9,31 @@
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\admin\class;
namespace app\service\admin\classroom;
use app\model\class\Class;
use app\model\classroom\Classroom;
use app\model\campus\Campus;
use app\model\personnel\Personnel;
use app\model\personnel\Personnel;
use core\base\BaseAdminService;
/**
* 班级服务层
* Class ClassService
* @package app\service\admin\class
* 场地管理服务层
* Class ClassroomService
* @package app\service\admin\classroom
*/
class ClassService extends BaseAdminService
class ClassroomService extends BaseAdminService
{
public function __construct()
{
parent::__construct();
$this->model = new Class();
$this->model = new Classroom();
}
/**
* 获取班级列表
* 获取场地管理列表
* @param array $where
* @return array
*/
@ -39,13 +42,13 @@ class ClassService extends BaseAdminService
$field = 'id,campus_id,campus_name,class_name,head_coach,age_group,class_type,assistant_coach,created_at,updated_at,deleted_at,status,sort_order,remarks';
$order = 'id desc';
$search_model = $this->model->withSearch(["id","campus_id","campus_name","class_name","head_coach","age_group","class_type","assistant_coach","created_at","updated_at","deleted_at","status","sort_order","remarks"], $where)->field($field)->order($order);
$search_model = $this->model->withSearch(["campus_id","class_name","head_coach","class_type","assistant_coach","created_at","status"], $where)->with(['campus','personnel','personnel'])->field($field)->order($order);
$list = $this->pageQuery($search_model);
return $list;
}
/**
* 获取班级信息
* 获取场地管理信息
* @param int $id
* @return array
*/
@ -53,12 +56,13 @@ class ClassService extends BaseAdminService
{
$field = 'id,campus_id,campus_name,class_name,head_coach,age_group,class_type,assistant_coach,created_at,updated_at,deleted_at,status,sort_order,remarks';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray();
$info = $this->model->field($field)->where([['id', "=", $id]])->with(['campus','personnel','personnel'])->findOrEmpty()->toArray();
$info['status'] = strval($info['status']);
return $info;
}
/**
* 添加班级
* 添加场地管理
* @param array $data
* @return mixed
*/
@ -70,7 +74,7 @@ class ClassService extends BaseAdminService
}
/**
* 班级编辑
* 场地管理编辑
* @param int $id
* @param array $data
* @return bool
@ -83,7 +87,7 @@ class ClassService extends BaseAdminService
}
/**
* 删除班级
* 删除场地管理
* @param int $id
* @return bool
*/
@ -95,5 +99,20 @@ class ClassService extends BaseAdminService
}
public function getCampusAll(){
$campusModel = new Campus();
return $campusModel->select()->toArray();
}
public function getPersonnelAll(){
$personnelModel = new Personnel();
return $personnelModel->select()->toArray();
}
public function getPersonnelAll(){
$personnelModel = new Personnel();
return $personnelModel->select()->toArray();
}
}

21
niucloud/app/service/admin/venue/VenueService.php

@ -40,7 +40,7 @@ class VenueService extends BaseAdminService
$field = 'id,campus_id,venue_name,capacity,availability_status,time_range_type,time_range_start,time_range_end,fixed_time_ranges,created_at,updated_at,deleted_at';
$order = 'updated_at desc';
$search_model = $this->model->withSearch(["campus_id","venue_name","capacity","availability_status","time_range_type","created_at","updated_at"], $where)->with(['campus'])->field($field)->order($order);
$search_model = $this->model->withSearch(["campus_id", "venue_name", "capacity", "availability_status", "time_range_type", "created_at", "updated_at"], $where)->with(['campus'])->field($field)->order($order);
$list = $this->pageQuery($search_model);
return $list;
}
@ -55,8 +55,9 @@ class VenueService extends BaseAdminService
$field = 'id,campus_id,venue_name,capacity,availability_status,time_range_type,time_range_start,time_range_end,fixed_time_ranges,created_at,updated_at,deleted_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->with(['campus'])->findOrEmpty()->toArray();
$info['availability_status'] = strval($info['availability_status']);
$info['time_range_type'] = strval($info['time_range_type']);
$info['availability_status'] = strval($info['availability_status']);
$info['time_range_type'] = strval($info['time_range_type']);
$info['fixed_time_ranges'] = json_decode($info['fixed_time_ranges'], true);
return $info;
}
@ -67,6 +68,10 @@ class VenueService extends BaseAdminService
*/
public function add(array $data)
{
if ($data['time_range_type'] === 'fixed') {
$data['fixed_time_ranges'] = json_encode($data['fixed_time_ranges']);
}
$res = $this->model->create($data);
return $res->id;
@ -80,6 +85,9 @@ class VenueService extends BaseAdminService
*/
public function edit(int $id, array $data)
{
if ($data['time_range_type'] === 'fixed') {
$data['fixed_time_ranges'] = json_encode($data['fixed_time_ranges']);
}
$this->model->where([['id', '=', $id]])->update($data);
return true;
@ -98,9 +106,10 @@ class VenueService extends BaseAdminService
}
public function getCampusAll(){
$campusModel = new Campus();
return $campusModel->select()->toArray();
public function getCampusAll()
{
$campusModel = new Campus();
return $campusModel->select()->toArray();
}

22
niucloud/app/validate/class/Class.php → niucloud/app/validate/classroom/Classroom.php

@ -9,43 +9,35 @@
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\validate\class;
namespace app\validate\classroom;
use core\base\BaseValidate;
/**
* 班级验证器
* Class Class
* @package addon\app\validate\class
* 场地管理验证器
* Class Classroom
* @package addon\app\validate\classroom
*/
class Class extends BaseValidate
class Classroom extends BaseValidate
{
protected $rule = [
'campus_id' => 'require',
'campus_name' => 'require',
'class_name' => 'require',
'head_coach' => 'require',
'age_group' => 'require',
'class_type' => 'require',
'assistant_coach' => 'require',
'status' => 'require',
'sort_order' => 'require',
];
protected $message = [
'campus_id.require' => ['common_validate.require', ['campus_id']],
'campus_name.require' => ['common_validate.require', ['campus_name']],
'class_name.require' => ['common_validate.require', ['class_name']],
'head_coach.require' => ['common_validate.require', ['head_coach']],
'age_group.require' => ['common_validate.require', ['age_group']],
'class_type.require' => ['common_validate.require', ['class_type']],
'assistant_coach.require' => ['common_validate.require', ['assistant_coach']],
'status.require' => ['common_validate.require', ['status']],
'sort_order.require' => ['common_validate.require', ['sort_order']],
];
protected $scene = [
"add" => ['campus_id', 'campus_name', 'class_name', 'head_coach', 'age_group', 'class_type', 'assistant_coach', 'created_at', 'updated_at', 'deleted_at', 'status', 'sort_order', 'remarks'],
"edit" => ['campus_id', 'campus_name', 'class_name', 'head_coach', 'age_group', 'class_type', 'assistant_coach', 'created_at', 'updated_at', 'deleted_at', 'status', 'sort_order', 'remarks']
"add" => ['campus_id', 'class_name', 'head_coach', 'age_group', 'class_type', 'assistant_coach', 'status', 'sort_order', 'remarks'],
"edit" => ['campus_id', 'class_name', 'head_coach', 'age_group', 'class_type', 'assistant_coach', 'status', 'sort_order', 'remarks']
];
}

12
niucloud/app/validate/personnel/Personnel.php

@ -28,12 +28,12 @@ class Personnel extends BaseValidate
];
protected $message = [
'name.require' => ['common_validate.require', ['name']],
'phone.require' => ['common_validate.require', ['phone']],
'phone.mobile' => ['common_validate.mobile', ['phone']],
'emergency_contact_phone.mobile' => ['common_validate.mobile', ['emergency_contact_phone']],
'status.require' => ['common_validate.require', ['status']],
'is_sys_user.require' => ['common_validate.require', ['is_sys_user']],
'name.require' => ['名称不能为空', ['name']],
'phone.require' => ['手机号不能为空', ['phone']],
'phone.mobile' => ['手机号格式错误', ['phone']],
'emergency_contact_phone.mobile' => ['紧急联系人手机号格式错误', ['emergency_contact_phone']],
'status.require' => ['状态不能为空', ['status']],
'is_sys_user.require' => ['是否登录系统不能为空', ['is_sys_user']],
];
protected $scene = [

Loading…
Cancel
Save