Browse Source

临时保存

master
王泽彦 10 months ago
parent
commit
e1b4415d1c
  1. 59
      admin/src/app/api/aaaaaaaaaaa.ts
  2. 3
      admin/src/app/api/venue.ts
  3. 196
      admin/src/app/views/aaaaaaaaaaa/aaaaaaaaaaa.vue
  4. 176
      admin/src/app/views/aaaaaaaaaaa/components/aaaaaaaaaaa-edit.vue
  5. 138
      admin/src/app/views/auth/components/edit-role.vue
  6. 151
      admin/src/app/views/auth/role.vue
  7. 434
      admin/src/app/views/timetables/components/schedule-add.vue
  8. 378
      admin/src/app/views/timetables/timetables.vue
  9. 3
      admin/src/components/TencentMapPicker.vue
  10. 75
      niucloud/app/adminapi/controller/venue/Venue.php
  11. 2
      niucloud/app/adminapi/route/venue.php
  12. 56
      niucloud/app/service/admin/venue/VenueService.php

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

@ -1,59 +0,0 @@
import request from '@/utils/request'
// USER_CODE_BEGIN -- aaaaaaaaaaa
/**
*
* @param params
* @returns
*/
export function getAaaaaaaaaaaList(params: Record<string, any>) {
return request.get(`aaaaaaaaaaa/aaaaaaaaaaa`, { params })
}
/**
*
* @param id id
* @returns
*/
export function getAaaaaaaaaaaInfo(id: number) {
return request.get(`aaaaaaaaaaa/aaaaaaaaaaa/${id}`)
}
/**
*
* @param params
* @returns
*/
export function addAaaaaaaaaaa(params: Record<string, any>) {
return request.post('aaaaaaaaaaa/aaaaaaaaaaa', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
}
/**
*
* @param id
* @param params
* @returns
*/
export function editAaaaaaaaaaa(params: Record<string, any>) {
return request.put(`aaaaaaaaaaa/aaaaaaaaaaa/${params.id}`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
}
/**
*
* @param id
* @returns
*/
export function deleteAaaaaaaaaaa(id: number) {
return request.delete(`aaaaaaaaaaa/aaaaaaaaaaa/${id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
}
// USER_CODE_END -- aaaaaaaaaaa

3
admin/src/app/api/venue.ts

@ -60,4 +60,7 @@ export function getWithCampusList(params: Record<string, any>) {
return request.get('venue/campus_all', { params })
}
export function getAllVenueList(params: Record<string, any>){
return request.get('venue/venue_all', { params })
}
// USER_CODE_END -- venue

196
admin/src/app/views/aaaaaaaaaaa/aaaaaaaaaaa.vue

@ -1,196 +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('addAaaaaaaaaaa') }}
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="aaaaaaaaaaaTable.searchParam"
ref="searchFormRef"
>
<el-form-item>
<el-button type="primary" @click="loadAaaaaaaaaaaList()">{{
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="aaaaaaaaaaaTable.data"
size="large"
v-loading="aaaaaaaaaaaTable.loading"
>
<template #empty>
<span>{{ !aaaaaaaaaaaTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :label="t('url1')" width="100" align="left">
<template #default="{ row }">
<el-avatar v-if="row.url1" :src="img(row.url1)" />
<el-avatar v-else icon="UserFilled" />
</template>
</el-table-column>
<el-table-column
prop="url2"
:label="t('url2')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="url3"
:label="t('url3')"
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="aaaaaaaaaaaTable.page"
v-model:page-size="aaaaaaaaaaaTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="aaaaaaaaaaaTable.total"
@size-change="loadAaaaaaaaaaaList()"
@current-change="loadAaaaaaaaaaaList"
/>
</div>
</div>
<edit ref="editAaaaaaaaaaaDialog" @complete="loadAaaaaaaaaaaList" />
</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 { getAaaaaaaaaaaList, deleteAaaaaaaaaaa } from '@/app/api/aaaaaaaaaaa'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import Edit from '@/app/views/aaaaaaaaaaa/components/aaaaaaaaaaa-edit.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
let aaaaaaaaaaaTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {},
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
/**
* 获取测试列表
*/
const loadAaaaaaaaaaaList = (page: number = 1) => {
aaaaaaaaaaaTable.loading = true
aaaaaaaaaaaTable.page = page
getAaaaaaaaaaaList({
page: aaaaaaaaaaaTable.page,
limit: aaaaaaaaaaaTable.limit,
...aaaaaaaaaaaTable.searchParam,
})
.then((res) => {
aaaaaaaaaaaTable.loading = false
aaaaaaaaaaaTable.data = res.data.data
aaaaaaaaaaaTable.total = res.data.total
})
.catch(() => {
aaaaaaaaaaaTable.loading = false
})
}
loadAaaaaaaaaaaList()
const editAaaaaaaaaaaDialog: Record<string, any> | null = ref(null)
/**
* 添加测试
*/
const addEvent = () => {
editAaaaaaaaaaaDialog.value.setFormData()
editAaaaaaaaaaaDialog.value.showDialog = true
}
/**
* 编辑测试
* @param data
*/
const editEvent = (data: any) => {
editAaaaaaaaaaaDialog.value.setFormData(data)
editAaaaaaaaaaaDialog.value.showDialog = true
}
/**
* 删除测试
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('aaaaaaaaaaaDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteAaaaaaaaaaa(id)
.then(() => {
loadAaaaaaaaaaaList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadAaaaaaaaaaaList()
}
</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>

176
admin/src/app/views/aaaaaaaaaaa/components/aaaaaaaaaaa-edit.vue

@ -1,176 +0,0 @@
<template>
<el-dialog
v-model="showDialog"
:title="formData.id ? t('updateAaaaaaaaaaa') : t('addAaaaaaaaaaa')"
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('url1')">
<upload-image v-model="formData.url1" />
</el-form-item>
<el-form-item :label="t('url2')">
<upload-file v-model="formData.url2" />
</el-form-item>
<el-form-item :label="t('url3')">
<upload-video v-model="formData.url3" />
</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 {
addAaaaaaaaaaa,
editAaaaaaaaaaa,
getAaaaaaaaaaaInfo,
} from '@/app/api/aaaaaaaaaaa'
let showDialog = ref(false)
const loading = ref(false)
/**
* 表单数据
*/
const initialFormData = {
id: '',
url1: '',
url2: '',
url3: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
url1: [{ required: true, message: t('url1Placeholder'), trigger: 'blur' }],
url2: [{ required: true, message: t('url2Placeholder'), trigger: 'blur' }],
url3: [{ required: true, message: t('url3Placeholder'), trigger: 'blur' }],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editAaaaaaaaaaa : addAaaaaaaaaaa
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 getAaaaaaaaaaaInfo(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>

138
admin/src/app/views/auth/components/edit-role.vue

@ -1,40 +1,39 @@
<template>
<el-dialog
v-model="showDialog"
:title="popTitle"
width="500px"
:destroy-on-close="true"
v-model="showDialog"
:title="popTitle"
width="500px"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="90px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
:model="formData"
label-width="90px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('roleName')" prop="role_name">
<el-input
v-model.trim="formData.role_name"
:placeholder="t('roleNamePlaceholder')"
clearable
:disabled="formData.uid"
class="input-width"
maxlength="10"
:show-word-limit="true"
v-model.trim="formData.role_name"
:placeholder="t('roleNamePlaceholder')"
clearable
:disabled="formData.uid"
class="input-width"
maxlength="10"
:show-word-limit="true"
/>
</el-form-item>
<el-form-item label="类型">
<el-radio-group v-model="formData.role_key">
<el-radio label="market">销售</el-radio>
<el-radio label="teacher">老师</el-radio>
<el-radio label="manager">主管</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="类型">
<el-radio-group v-model="formData.role_key">
<span v-for="(item,index) in roleKeyList" class="mr-2">
<el-radio :label="item.value">{{ item.name }}</el-radio>
</span>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('status')">
<el-radio-group v-model="formData.status">
@ -46,24 +45,25 @@
<el-form-item :label="t('permission')" prop="rules">
<div class="flex items-center justify-between w-11/12">
<div>
<el-checkbox v-model="selectAll" :label="t('selectAll')" />
<el-checkbox v-model="checkStrictly" :label="t('checkStrictly')" />
<el-checkbox v-model="selectAll" :label="t('selectAll')"/>
<el-checkbox v-model="checkStrictly" :label="t('checkStrictly')"/>
</div>
<el-button link type="primary" @click="menuAction()">{{
t('foldText')
}}</el-button>
t('foldText')
}}
</el-button>
</div>
<el-scrollbar height="35vh" class="w-full">
<el-tree
:data="menus"
:props="{ label: 'menu_name' }"
:default-checked-keys="formData.rules"
:check-strictly="checkStrictly"
show-checkbox
default-expand-all
@check-change="handleCheckChange"
node-key="menu_key"
ref="treeRef"
:data="menus"
:props="{ label: 'menu_name' }"
:default-checked-keys="formData.rules"
:check-strictly="checkStrictly"
show-checkbox
default-expand-all
@check-change="handleCheckChange"
node-key="menu_key"
ref="treeRef"
/>
</el-scrollbar>
</el-form-item>
@ -73,10 +73,10 @@
<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
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
@ -84,13 +84,13 @@
</template>
<script lang="ts" setup async>
import { ref, reactive, computed, watch, toRaw } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { addRole, editRole, getRoleInfo } from '@/app/api/sys'
import { getAuthMenus } from '@/app/api/auth'
import { debounce } from '@/utils/common'
import {ref, reactive, computed, watch, toRaw} from 'vue'
import {t} from '@/lang'
import type {FormInstance} from 'element-plus'
import {addRole, editRole, getRoleInfo} from '@/app/api/sys'
import {getAuthMenus} from '@/app/api/auth'
import {useDictionary} from '@/app/api/dict'
import {debounce} from '@/utils/common'
const showDialog = ref(false)
const loading = ref(false)
@ -100,7 +100,7 @@ let popTitle: string = ''
//
const menus = ref<Record<string, any>[]>([])
getAuthMenus({ is_button: 0 }).then((res) => {
getAuthMenus({is_button: 0}).then((res) => {
menus.value = res.data
})
@ -116,6 +116,14 @@ watch(selectAll, () => {
}
})
const roleKeyList = ref([])
const getRolekeyDictList = async () => {
roleKeyList.value = await (
await useDictionary('role_key')
).data.dictionary
}
getRolekeyDictList()
const handleCheckChange = debounce((e) => {
formData.rules = treeRef.value.getCheckedKeys()
})
@ -153,10 +161,10 @@ const initialFormData = {
role_id: 0,
role_name: '',
status: 1,
role_key:'',
role_key: '',
rules: [],
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formData: Record<string, any> = reactive({...initialFormData})
const formRef = ref<FormInstance>()
@ -164,7 +172,7 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
role_name: [
{ required: true, message: t('roleNamePlaceholder'), trigger: 'blur' },
{required: true, message: t('roleNamePlaceholder'), trigger: 'blur'},
],
rules: [
{
@ -196,16 +204,16 @@ const confirm = async (formEl: FormInstance | undefined) => {
data.rules = data.rules.concat(treeRef.value.getHalfCheckedKeys())
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch(() => {
loading.value = false
// showDialog.value = false
})
emit('complete')
})
.catch(() => {
loading.value = false
// showDialog.value = false
})
}
})
}

151
admin/src/app/views/auth/role.vue

@ -8,82 +8,90 @@
<div class="flex justify-between items-center mt-[20px]">
<el-form
:inline="true"
:model="roleTableData.searchParam"
ref="searchFormRef"
:inline="true"
:model="roleTableData.searchParam"
ref="searchFormRef"
>
<el-form-item :label="t('roleName')" prop="search">
<el-input
v-model.trim="roleTableData.searchParam.search"
class="w-[240px]"
:placeholder="t('roleNamePlaceholder')"
v-model.trim="roleTableData.searchParam.search"
class="w-[240px]"
:placeholder="t('roleNamePlaceholder')"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadRoleList()">{{
t('search')
}}</el-button>
t('search')
}}
</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
t('reset')
}}
</el-button>
</el-form-item>
</el-form>
<el-button
type="primary"
class="w-[100px] self-start"
@click="addEvent"
>{{ t('addRole') }}</el-button
type="primary"
class="w-[100px] self-start"
@click="addEvent"
>{{ t('addRole') }}
</el-button
>
</div>
<div>
<el-table
:data="roleTableData.data"
size="large"
v-loading="roleTableData.loading"
:data="roleTableData.data"
size="large"
v-loading="roleTableData.loading"
>
<template #empty>
<span>{{ !roleTableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="role_name" :label="t('roleName')" />
<el-table-column prop="role_name" :label="t('roleName')"/>
<el-table-column :label="t('status')">
<template #default="{ row }">
<el-tag type="success" v-if="row.status == 1">{{
row.status_name
}}</el-tag>
row.status_name
}}
</el-tag>
<el-tag type="error" v-if="row.status == 0">{{
row.status_name
}}</el-tag>
row.status_name
}}
</el-tag>
</template>
</el-table-column>
<el-table-column label="类型">
<template #default="{ row }">
<el-tag type="success" v-if="row.role_key == 'market'">销售</el-tag>
<el-tag type="success" v-if="row.role_key == 'teacher'">老师</el-tag>
<el-tag type="success" v-if="row.role_key == 'manager'">主管</el-tag>
</template>
</el-table-column>
<el-table-column label="类型">
<template #default="{ row }">
<span v-for="(item,index) in roleKeyList" :key="index">
<el-tag type="success" v-if="row.role_key == item.value">{{ item.name }}</el-tag>
</span>
</template>
</el-table-column>
<el-table-column
prop="create_time"
:label="t('createTime')"
prop="create_time"
:label="t('createTime')"
></el-table-column>
<el-table-column
:label="t('operation')"
align="right"
fixed="right"
width="130"
:label="t('operation')"
align="right"
fixed="right"
width="130"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
t('edit')
}}
</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.role_id)"
>{{ t('delete') }}</el-button
type="primary"
link
@click="deleteEvent(row.role_id)"
>{{ t('delete') }}
</el-button
>
</template>
</el-table-column>
@ -91,28 +99,29 @@
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="roleTableData.page"
v-model:page-size="roleTableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="roleTableData.total"
@size-change="loadRoleList()"
@current-change="loadRoleList"
v-model:current-page="roleTableData.page"
v-model:page-size="roleTableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="roleTableData.total"
@size-change="loadRoleList()"
@current-change="loadRoleList"
/>
</div>
</div>
<edit-role ref="editRoleDialog" @complete="loadRoleList()" />
<edit-role ref="editRoleDialog" @complete="loadRoleList()"/>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { t } from '@/lang'
import { getRoleList, deleteRole } from '@/app/api/sys'
import { ElMessageBox, FormInstance } from 'element-plus'
import {ref, reactive} from 'vue'
import {t} from '@/lang'
import {getRoleList, deleteRole} from '@/app/api/sys'
import {ElMessageBox, FormInstance} from 'element-plus'
import EditRole from '@/app/views/auth/components/edit-role.vue'
import { useRoute } from 'vue-router'
import {useRoute} from 'vue-router'
import {useDictionary} from '@/app/api/dict'
const route = useRoute()
const pageName = route.meta.title
@ -134,6 +143,13 @@ const resetForm = (formEl: FormInstance | undefined) => {
formEl.resetFields()
loadRoleList()
}
const roleKeyList = ref([])
const getRolekeyDictList = async () => {
roleKeyList.value = await (
await useDictionary('role_key')
).data.dictionary
}
getRolekeyDictList()
/**
* 获取角色列表
*/
@ -146,14 +162,14 @@ const loadRoleList = (page: number = 1) => {
limit: roleTableData.limit,
role_name: roleTableData.searchParam.search,
})
.then((res) => {
roleTableData.loading = false
roleTableData.data = res.data.data
roleTableData.total = res.data.total
})
.catch(() => {
roleTableData.loading = false
})
.then((res) => {
roleTableData.loading = false
roleTableData.data = res.data.data
roleTableData.total = res.data.total
})
.catch(() => {
roleTableData.loading = false
})
}
loadRoleList()
@ -186,10 +202,11 @@ const deleteEvent = (id: number) => {
type: 'warning',
}).then(() => {
deleteRole(id)
.then(() => {
loadRoleList()
})
.catch(() => {})
.then(() => {
loadRoleList()
})
.catch(() => {
})
})
}
</script>

434
admin/src/app/views/timetables/components/schedule-add.vue

@ -0,0 +1,434 @@
<template>
<el-dialog v-model="dialogVisible" title="添加课程" width="600px">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="120px"
>
<el-form-item label="校区" prop="campus_id">
<el-select
v-model="form.campus_id"
placeholder="请选择校区"
@change="handleCampusChange"
>
<el-option
v-for="item in campusList"
:key="item.id"
:label="item.campus_name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="场地" prop="venue_id">
<el-select
v-model="form.venue_id"
placeholder="请选择场地"
@change="calculateAvailableCapacity"
>
<el-option
v-for="item in venueList"
:key="item.id"
:label="item.venue_name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="上课日期" prop="course_date">
<el-date-picker
v-model="form.course_date"
type="date"
placeholder="选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
:disabled-date="disabledDate"
/>
</el-form-item>
<el-form-item label="上课时段" prop="time_slot">
<el-select
v-model="form.time_slot"
placeholder="请选择上课时段"
@change="calculateAvailableCapacity"
>
<el-option
v-for="(timeSlot, index) in timeSlotOptions"
:key="index"
:label="timeSlot"
:value="timeSlot"
/>
</el-select>
</el-form-item>
<el-form-item label="剩余空位" v-if="form.venue_id && form.time_slot">
<span>{{ availableCapacity }}</span>
</el-form-item>
<el-form-item label="上课类型" prop="course_type">
<el-radio-group v-model="form.course_type" @change="handleCourseTypeChange">
<el-radio label="class">班级</el-radio>
<el-radio label="student">学员</el-radio>
<el-radio label="trial">试课</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="班级" prop="class_ids" v-if="form.course_type === 'class'">
<el-checkbox-group v-model="form.class_ids" @change="handleClassChange">
<el-checkbox
v-for="item in classList"
:key="item.id"
:label="item.id"
>
{{ item.class_name }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="学员" prop="student_ids" v-if="form.course_type === 'student' || form.course_type === 'trial'">
<el-input
v-model="studentSearchKeyword"
placeholder="搜索学员名称"
@input="searchStudents"
/>
<div class="student-checkbox-list">
<el-checkbox-group v-model="form.student_ids">
<el-checkbox
v-for="item in filteredStudentList"
:key="item.id"
:label="item.id"
>
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</el-form-item>
<el-form-item label="课程名称" prop="course_name">
<el-input v-model="form.course_name" placeholder="请输入课程名称" />
</el-form-item>
<el-form-item label="上课教练" prop="coach_id">
<el-select v-model="form.coach_id" placeholder="请选择教练">
<el-option
v-for="item in coachList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submit">确定</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, defineProps, defineEmits, watch } from 'vue'
import {getAllVenueList, getVenueList} from '@/app/api/venue'
import { getClassroomList, getWithPersonnelList } from '@/app/api/classroom'
import { addCourseSchedule } from '@/app/api/course_schedule'
import { getTimetables } from '@/app/api/course_schedule'
import { ElMessage } from 'element-plus'
const props = defineProps({
visible: {
type: Boolean,
default: false
},
campusList: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:visible', 'success'])
//
const dialogVisible = ref(false)
const formRef = ref(null)
const venueList = ref([])
const classList = ref([])
const studentList = ref([])
const coachList = ref([])
const timeSlotOptions = ref(['9:00-10:00', '10:00-11:00', '11:00-12:00', '14:00-15:00', '15:00-16:00', '16:00-17:00'])
const availableCapacity = ref(0)
const studentSearchKeyword = ref('')
const filteredStudentList = ref([])
// visible
watch(() => props.visible, (newVal) => {
dialogVisible.value = newVal
})
// dialogVisible
watch(dialogVisible, (newVal) => {
emit('update:visible', newVal)
if (newVal) {
//
loadCoachList()
if (form.value.campus_id) {
handleCampusChange()
}
}
})
const form = ref({
campus_id: '',
venue_id: '',
course_date: '',
time_slot: '',
course_type: 'class',
class_ids: [],
student_ids: [],
course_name: '',
coach_id: ''
})
const rules = {
campus_id: [{ required: true, message: '请选择校区', trigger: 'change' }],
venue_id: [{ required: true, message: '请选择场地', trigger: 'change' }],
course_date: [{ required: true, message: '请选择上课日期', trigger: 'change' }],
time_slot: [{ required: true, message: '请选择上课时段', trigger: 'change' }],
course_type: [{ required: true, message: '请选择上课类型', trigger: 'change' }],
coach_id: [{ required: true, message: '请选择上课教练', trigger: 'change' }],
course_name: [{ required: true, message: '请输入课程名称', trigger: 'blur' }]
}
//
const disabledDate = (time) => {
return time.getTime() < Date.now() - 8.64e7 //
}
//
const handleCampusChange = async () => {
//
try {
const response = await getAllVenueList({ campus_id: form.value.campus_id })
venueList.value = response.data
} catch (error) {
console.error('获取场地列表失败:', error)
}
//
try {
const response = await getClassroomList({ campus_id: form.value.campus_id })
if (response.data && response.data.list) {
classList.value = response.data.list
}
} catch (error) {
console.error('获取班级列表失败:', error)
}
//
form.value.venue_id = ''
form.value.class_ids = []
availableCapacity.value = 0
}
//
const calculateAvailableCapacity = async () => {
if (!form.value.venue_id || !form.value.time_slot) {
availableCapacity.value = 0
return
}
try {
//
const venueInfo = venueList.value.find(item => item.id === form.value.venue_id)
if (venueInfo) {
//
availableCapacity.value = venueInfo.capacity || 0
//
if (form.value.course_date) {
const params = {
venue_id: form.value.venue_id,
course_date: form.value.course_date,
time_slot: form.value.time_slot
}
const response = await getTimetables(params)
if (response.data && response.data.length > 0) {
//
const matchingDay = response.data.find(day =>
day.date.includes(form.value.course_date)
)
if (matchingDay) {
const matchingTimeSlot = matchingDay.timeSlots.find(slot =>
slot.timeRange === form.value.time_slot
)
if (matchingTimeSlot && matchingTimeSlot.course) {
//
availableCapacity.value = matchingTimeSlot.course.hasnumber
}
}
}
}
}
} catch (error) {
console.error('计算可用容量失败:', error)
}
}
//
const handleCourseTypeChange = () => {
//
form.value.class_ids = []
form.value.student_ids = []
form.value.course_name = ''
//
if (form.value.course_type === 'student' || form.value.course_type === 'trial') {
loadStudentList()
}
}
//
const handleClassChange = () => {
//
if (form.value.class_ids.length > 0) {
const selectedClass = classList.value.find(item => item.id === form.value.class_ids[0])
if (selectedClass) {
form.value.course_name = selectedClass.class_name
}
}
}
//
const loadStudentList = async () => {
try {
//
// 使
studentList.value = [
{ id: 1, name: '学员1' },
{ id: 2, name: '学员2' },
{ id: 3, name: '学员3' }
]
filteredStudentList.value = [...studentList.value]
} catch (error) {
console.error('获取学员列表失败:', error)
}
}
//
const loadCoachList = async () => {
try {
// 使classroom
const response = await getWithPersonnelList({})
if (response.data) {
coachList.value = response.data
} else {
//
coachList.value = [
{ id: 1, name: '教练1' },
{ id: 2, name: '教练2' },
{ id: 3, name: '教练3' }
]
}
} catch (error) {
console.error('获取教练列表失败:', error)
//
coachList.value = [
{ id: 1, name: '教练1' },
{ id: 2, name: '教练2' },
{ id: 3, name: '教练3' }
]
}
}
//
const searchStudents = () => {
if (!studentSearchKeyword.value) {
filteredStudentList.value = [...studentList.value]
return
}
filteredStudentList.value = studentList.value.filter(student =>
student.name.includes(studentSearchKeyword.value)
)
}
//
const cancel = () => {
dialogVisible.value = false
}
//
const submit = () => {
formRef.value.validate(async (valid) => {
if (!valid) return
try {
const params = {
campus_id: form.value.campus_id,
venue_id: form.value.venue_id,
course_date: form.value.course_date,
time_slot: form.value.time_slot,
course_id: 0, //
coach_id: form.value.coach_id,
participants: form.value.course_type,
available_capacity: availableCapacity.value,
status: 'active',
}
//
if (form.value.course_type === 'class') {
params.class_ids = form.value.class_ids
//
const classStudents = []
//
params.student_ids = classStudents
} else {
params.student_ids = form.value.student_ids
}
const response = await addCourseSchedule(params)
if (response.code === 0) {
ElMessage.success('添加课程成功')
dialogVisible.value = false
emit('success')
resetForm()
}
} catch (error) {
console.error('添加课程失败:', error)
ElMessage.error('添加课程失败')
}
})
}
//
const resetForm = () => {
form.value = {
campus_id: '',
venue_id: '',
course_date: '',
time_slot: '',
course_type: 'class',
class_ids: [],
student_ids: [],
course_name: '',
coach_id: ''
}
if (formRef.value) {
formRef.value.resetFields()
}
}
</script>
<style scoped>
.student-checkbox-list {
max-height: 200px;
overflow-y: auto;
border: 1px solid #EBEEF5;
border-radius: 4px;
padding: 10px;
margin-top: 8px;
}
</style>

378
admin/src/app/views/timetables/timetables.vue

@ -101,132 +101,12 @@
</p>
</el-dialog>
<!-- 添加课程弹窗 -->
<el-dialog v-model="addDialogVisible" title="添加课程" width="600px">
<el-form
ref="addFormRef"
:model="addForm"
:rules="addFormRules"
label-width="120px"
>
<el-form-item label="校区" prop="campus_id">
<el-select
v-model="addForm.campus_id"
placeholder="请选择校区"
@change="handleAddCampusChange"
>
<el-option
v-for="item in campusList"
:key="item.id"
:label="item.campus_name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="场地" prop="venue_id">
<el-select
v-model="addForm.venue_id"
placeholder="请选择场地"
@change="calculateAvailableCapacity"
>
<el-option
v-for="item in venueList"
:key="item.id"
:label="item.venue_name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="上课日期" prop="course_date">
<el-date-picker
v-model="addForm.course_date"
type="date"
placeholder="选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="上课时段" prop="time_slot">
<el-select
v-model="addForm.time_slot"
placeholder="请选择上课时段"
@change="calculateAvailableCapacity"
>
<el-option
v-for="(timeSlot, index) in timeSlotOptions"
:key="index"
:label="timeSlot"
:value="timeSlot"
/>
</el-select>
</el-form-item>
<el-form-item label="剩余空位" v-if="addForm.venue_id && addForm.time_slot">
<span>{{ availableCapacity }}</span>
</el-form-item>
<el-form-item label="上课类型" prop="course_type">
<el-radio-group v-model="addForm.course_type" @change="handleCourseTypeChange">
<el-radio label="class">班级</el-radio>
<el-radio label="student">学员</el-radio>
<el-radio label="trial">试课</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="班级" prop="class_ids" v-if="addForm.course_type === 'class'">
<el-checkbox-group v-model="addForm.class_ids" @change="handleClassChange">
<el-checkbox
v-for="item in classList"
:key="item.id"
:label="item.id"
>
{{ item.class_name }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="学员" prop="student_ids" v-if="addForm.course_type === 'student' || addForm.course_type === 'trial'">
<el-input
v-model="studentSearchKeyword"
placeholder="搜索学员名称"
@input="searchStudents"
/>
<div class="student-checkbox-list">
<el-checkbox-group v-model="addForm.student_ids">
<el-checkbox
v-for="item in filteredStudentList"
:key="item.id"
:label="item.id"
>
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</el-form-item>
<el-form-item label="课程名称" prop="course_name">
<el-input v-model="addForm.course_name" placeholder="请输入课程名称" />
</el-form-item>
<el-form-item label="上课教练" prop="coach_id">
<el-select v-model="addForm.coach_id" placeholder="请选择教练">
<el-option
v-for="item in coachList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitAddForm">确定</el-button>
</template>
</el-dialog>
<!-- 添加课程弹窗组件 -->
<ScheduleAdd
v-model:visible="addDialogVisible"
:campusList="campusList"
@success="fetchData"
/>
</div>
</el-card>
</template>
@ -235,48 +115,14 @@
import { ref, onMounted, computed } from 'vue'
import { getTimetables } from '@/app/api/course_schedule'
import { getWithCampusList } from '@/app/api/venue'
import { getVenueList } from '@/app/api/venue'
import { getClassroomList, getWithPersonnelList } from '@/app/api/classroom'
import { addCourseSchedule } from '@/app/api/course_schedule'
import { ElMessage } from 'element-plus'
import ScheduleAdd from './components/schedule-add.vue'
//
const campusList = ref([])
const selectedCampus = ref('')
//
//
const addDialogVisible = ref(false)
const addFormRef = ref(null)
const venueList = ref([])
const classList = ref([])
const studentList = ref([])
const coachList = ref([])
const timeSlotOptions = ref(['9:00-10:00', '10:00-11:00', '11:00-12:00', '14:00-15:00', '15:00-16:00', '16:00-17:00'])
const availableCapacity = ref(0)
const studentSearchKeyword = ref('')
const filteredStudentList = ref([])
const addForm = ref({
campus_id: '',
venue_id: '',
course_date: '',
time_slot: '',
course_type: 'class',
class_ids: [],
student_ids: [],
course_name: '',
coach_id: ''
})
const addFormRules = {
campus_id: [{ required: true, message: '请选择校区', trigger: 'change' }],
venue_id: [{ required: true, message: '请选择场地', trigger: 'change' }],
course_date: [{ required: true, message: '请选择上课日期', trigger: 'change' }],
time_slot: [{ required: true, message: '请选择上课时段', trigger: 'change' }],
course_type: [{ required: true, message: '请选择上课类型', trigger: 'change' }],
coach_id: [{ required: true, message: '请选择上课教练', trigger: 'change' }],
course_name: [{ required: true, message: '请输入课程名称', trigger: 'blur' }]
}
//
const weekDate = ref(new Date())
@ -425,209 +271,10 @@ const handleCellClick = (row, column, cell, event) => {
}
}
//
const handleAddCampusChange = async () => {
//
try {
const response = await getVenueList({ campus_id: addForm.value.campus_id })
if (response.data && response.data.list) {
venueList.value = response.data.list
}
} catch (error) {
console.error('获取场地列表失败:', error)
}
//
try {
const response = await getClassroomList({ campus_id: addForm.value.campus_id })
if (response.data && response.data.list) {
classList.value = response.data.list
}
} catch (error) {
console.error('获取班级列表失败:', error)
}
//
addForm.value.venue_id = ''
addForm.value.class_ids = []
availableCapacity.value = 0
}
const calculateAvailableCapacity = async () => {
if (!addForm.value.venue_id || !addForm.value.time_slot) {
availableCapacity.value = 0
return
}
try {
//
const venueInfo = venueList.value.find(item => item.id === addForm.value.venue_id)
if (venueInfo) {
//
availableCapacity.value = venueInfo.capacity || 0
//
if (addForm.value.course_date) {
const params = {
venue_id: addForm.value.venue_id,
course_date: addForm.value.course_date,
time_slot: addForm.value.time_slot
}
const response = await getTimetables(params)
if (response.data && response.data.length > 0) {
//
const matchingDay = response.data.find(day =>
day.date.includes(addForm.value.course_date)
)
if (matchingDay) {
const matchingTimeSlot = matchingDay.timeSlots.find(slot =>
slot.timeRange === addForm.value.time_slot
)
if (matchingTimeSlot && matchingTimeSlot.course) {
//
availableCapacity.value = matchingTimeSlot.course.hasnumber
}
}
}
}
}
} catch (error) {
console.error('计算可用容量失败:', error)
}
}
const handleCourseTypeChange = () => {
//
addForm.value.class_ids = []
addForm.value.student_ids = []
addForm.value.course_name = ''
//
if (addForm.value.course_type === 'student' || addForm.value.course_type === 'trial') {
loadStudentList()
}
}
const handleClassChange = () => {
//
if (addForm.value.class_ids.length > 0) {
const selectedClass = classList.value.find(item => item.id === addForm.value.class_ids[0])
if (selectedClass) {
addForm.value.course_name = selectedClass.class_name
}
}
}
//
const loadStudentList = async () => {
try {
//
// 使
// const response = await fetch('/student_courses/student_all')
// const data = await response.json()
// if (data.data) {
// studentList.value = data.data
// filteredStudentList.value = [...studentList.value]
// }
//
studentList.value = [
{ id: 1, name: '学员1' },
{ id: 2, name: '学员2' },
{ id: 3, name: '学员3' }
]
filteredStudentList.value = [...studentList.value]
} catch (error) {
console.error('获取学员列表失败:', error)
}
}
//
const loadCoachList = async () => {
try {
// 使classroom
const response = await getWithPersonnelList({})
if (response.data) {
coachList.value = response.data
} else {
//
coachList.value = [
{ id: 1, name: '教练1' },
{ id: 2, name: '教练2' },
{ id: 3, name: '教练3' }
]
}
} catch (error) {
console.error('获取教练列表失败:', error)
//
coachList.value = [
{ id: 1, name: '教练1' },
{ id: 2, name: '教练2' },
{ id: 3, name: '教练3' }
]
}
}
const searchStudents = () => {
if (!studentSearchKeyword.value) {
filteredStudentList.value = [...studentList.value]
return
}
filteredStudentList.value = studentList.value.filter(student =>
student.name.includes(studentSearchKeyword.value)
)
}
const submitAddForm = () => {
addFormRef.value.validate(async (valid) => {
if (!valid) return
try {
const params = {
campus_id: addForm.value.campus_id,
venue_id: addForm.value.venue_id,
course_date: addForm.value.course_date,
time_slot: addForm.value.time_slot,
course_id: 0, //
coach_id: addForm.value.coach_id,
participants: addForm.value.course_type,
available_capacity: availableCapacity.value,
status: 'active',
}
//
if (addForm.value.course_type === 'class') {
params.class_ids = addForm.value.class_ids
//
const classStudents = []
//
params.student_ids = classStudents
} else {
params.student_ids = addForm.value.student_ids
}
const response = await addCourseSchedule(params)
if (response.code === 0) {
ElMessage.success('添加课程成功')
addDialogVisible.value = false
fetchData() //
}
} catch (error) {
console.error('添加课程失败:', error)
ElMessage.error('添加课程失败')
}
})
}
//
onMounted(() => {
fetchCampusList()
fetchData()
loadCoachList()
})
</script>
@ -671,13 +318,4 @@ onMounted(() => {
.week-picker {
width: 180px;
}
.student-checkbox-list {
max-height: 200px;
overflow-y: auto;
border: 1px solid #EBEEF5;
border-radius: 4px;
padding: 10px;
margin-top: 8px;
}
</style>

3
admin/src/components/TencentMapPicker.vue

@ -315,8 +315,7 @@ const cleanupMap = () => {
const loadMapSDK = (): Promise<void> => {
return new Promise((resolve, reject) => {
mapKey.value = 'AKTBZ-OGICT-E5NXQ-LGEGK-H5AJ5-M2BOX'
mapKey.value = import.meta.env.VITE_MAP_KEY
mapScript = document.createElement('script')
mapScript.type = 'text/javascript'
mapScript.src = `https://map.qq.com/api/gljs?libraries=tools ,service&v=1.exp&key=${mapKey.value}`

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

@ -22,19 +22,20 @@ use app\service\admin\venue\VenueService;
*/
class Venue extends BaseAdminController
{
/**
* 获取场地列表
* @return \think\Response
*/
public function lists(){
/**
* 获取场地列表
* @return \think\Response
*/
public function lists()
{
$data = $this->request->params([
["campus_id",""],
["venue_name",""],
["capacity",""],
["availability_status",""],
["time_range_type",""],
["created_at",""],
["updated_at",""]
["campus_id", ""],
["venue_name", ""],
["capacity", ""],
["availability_status", ""],
["time_range_type", ""],
["created_at", ""],
["updated_at", ""]
]);
return success((new VenueService())->getPage($data));
}
@ -44,7 +45,8 @@ class Venue extends BaseAdminController
* @param int $id
* @return \think\Response
*/
public function info(int $id){
public function info(int $id)
{
return success((new VenueService())->getInfo($id));
}
@ -52,14 +54,15 @@ class Venue extends BaseAdminController
* 添加场地
* @return \think\Response
*/
public function add(){
public function add()
{
$data = $this->request->params([
["campus_id",0],
["venue_name",""],
["capacity",0],
["availability_status",0],
["time_range_type",""],
["fixed_time_ranges",[]],
["campus_id", 0],
["venue_name", ""],
["capacity", 0],
["availability_status", 0],
["time_range_type", ""],
["fixed_time_ranges", []],
]);
$this->validate($data, 'app\validate\venue\Venue.add');
@ -72,14 +75,15 @@ class Venue extends BaseAdminController
* @param $id 场地id
* @return \think\Response
*/
public function edit(int $id){
public function edit(int $id)
{
$data = $this->request->params([
["campus_id",0],
["venue_name",""],
["capacity",0],
["availability_status",0],
["time_range_type",""],
["fixed_time_ranges",[]],
["campus_id", 0],
["venue_name", ""],
["capacity", 0],
["availability_status", 0],
["time_range_type", ""],
["fixed_time_ranges", []],
]);
$this->validate($data, 'app\validate\venue\Venue.edit');
@ -92,14 +96,23 @@ class Venue extends BaseAdminController
* @param $id 场地id
* @return \think\Response
*/
public function del(int $id){
public function del(int $id)
{
(new VenueService())->del($id);
return success('DELETE_SUCCESS');
}
public function getCampusAll(){
return success(( new VenueService())->getCampusAll());
public function getCampusAll()
{
return success((new VenueService())->getCampusAll());
}
public function getVenueAll()
{
$data = $this->request->params([
["campus_id", 0],
]);
return success((new VenueService())->getVenueAll($data['campus_id']));
}
}

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

@ -34,6 +34,8 @@ Route::group('venue', function () {
Route::get('campus_all','venue.Venue/getCampusAll');
Route::get('venue_all','venue.Venue/getVenueAll');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,

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

@ -106,11 +106,65 @@ class VenueService extends BaseAdminService
}
/**
* 获取校区列表
* @return array
*/
public function getCampusAll()
{
$campusModel = new Campus();
return $campusModel->select()->toArray();
return $campusModel->select()
->where('campus_status', '=', 1)
->toArray();
}
/**
* 获取校区下的场地列表
* @param $campus_id
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function getVenueAll($campus_id)
{
return $this->model->where('availability_status', '=', 1)
->where('campus_id', $campus_id)
->select()
->toArray();
}
/**
* 获取场地那天的可预约时间
*/
public function getVenueTime($venue_id, $date)
{
$venue_info = $this->model->where('id', '=', $venue_id)->find();
$venue_info['time_range'] = [];
//可用时间范围
if ($venue_info['time_range_type'] === 'range') {
$time_range_start = $venue_info['time_range_start'];
$time_range_end = $venue_info['time_range_end'];
for ($i = $time_range_start; $i <= $time_range_end; $i++) {
$venue_info['time_range'][] = $i;
}
}
// 固定使用时间
if ($venue_info['time_range_type'] === 'fixed') {
$venue_info['time_range'] = json_decode($venue_info['fixed_time_ranges'], true);
for ($i = 0; $i < count($venue_info['time_range']); $i++) {
$venue_info['time_range'][$i]['start'] = strtotime($date . ' ' . $venue_info['time_range'][$i]['start_time']);
$venue_info['time_range'][$i]['end'] = strtotime($date . ' ' . $venue_info['time_range'][$i]['end_time']);
}
}
// 全天可用从早上8点开始到晚上22点结束
if ($venue_info['time_range_type'] === 'all') {
for ($i = 8; $i <= 22; $i++) {
$venue_info['time_range'][] = $i;
}
}
return $venue_info;
}
}

Loading…
Cancel
Save