Browse Source

修改班级和排课

wangzeyan
王泽彦 11 months ago
parent
commit
89b9e21057
  1. 58
      admin/package-lock.json
  2. 7
      admin/package.json
  3. 562
      admin/src/app/views/classroom/classroom.vue
  4. 577
      admin/src/app/views/classroom/components/classroom-edit.vue
  5. 127
      admin/src/app/views/timetables/timetables.vue
  6. 81
      admin/src/app/views/yjpz_config/yjpz_config.vue
  7. 7620
      admin/yarn.lock

58
admin/package-lock.json

@ -9,6 +9,11 @@
"version": "1.0.0",
"dependencies": {
"@element-plus/icons-vue": "2.0.10",
"@fullcalendar/core": "^6.1.17",
"@fullcalendar/daygrid": "^6.1.17",
"@fullcalendar/interaction": "^6.1.17",
"@fullcalendar/timegrid": "^6.1.17",
"@fullcalendar/vue3": "^6.1.17",
"@highlightjs/vue-plugin": "2.1.0",
"@types/lodash-es": "4.17.6",
"@vueuse/core": "9.12.0",
@ -962,6 +967,50 @@
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.1.6.tgz",
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
},
"node_modules/@fullcalendar/core": {
"version": "6.1.17",
"resolved": "https://registry.npmmirror.com/@fullcalendar/core/-/core-6.1.17.tgz",
"integrity": "sha512-0W7lnIrv18ruJ5zeWBeNZXO8qCWlzxDdp9COFEsZnyNjiEhUVnrW/dPbjRKYpL0edGG0/Lhs0ghp1z/5ekt8ZA==",
"dependencies": {
"preact": "~10.12.1"
}
},
"node_modules/@fullcalendar/daygrid": {
"version": "6.1.17",
"resolved": "https://registry.npmmirror.com/@fullcalendar/daygrid/-/daygrid-6.1.17.tgz",
"integrity": "sha512-K7m+pd7oVJ9fW4h7CLDdDGJbc9szJ1xDU1DZ2ag+7oOo1aCNLv44CehzkkknM6r8EYlOOhgaelxQpKAI4glj7A==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.17"
}
},
"node_modules/@fullcalendar/interaction": {
"version": "6.1.17",
"resolved": "https://registry.npmmirror.com/@fullcalendar/interaction/-/interaction-6.1.17.tgz",
"integrity": "sha512-AudvQvgmJP2FU89wpSulUUjeWv24SuyCx8FzH2WIPVaYg+vDGGYarI7K6PcM3TH7B/CyaBjm5Rqw9lXgnwt5YA==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.17"
}
},
"node_modules/@fullcalendar/timegrid": {
"version": "6.1.17",
"resolved": "https://registry.npmmirror.com/@fullcalendar/timegrid/-/timegrid-6.1.17.tgz",
"integrity": "sha512-K4PlA3L3lclLOs3IX8cvddeiJI9ZVMD7RA9IqaWwbvac771971foc9tFze9YY+Pqesf6S+vhS2dWtEVlERaGlQ==",
"dependencies": {
"@fullcalendar/daygrid": "~6.1.17"
},
"peerDependencies": {
"@fullcalendar/core": "~6.1.17"
}
},
"node_modules/@fullcalendar/vue3": {
"version": "6.1.17",
"resolved": "https://registry.npmmirror.com/@fullcalendar/vue3/-/vue3-6.1.17.tgz",
"integrity": "sha512-0+qi/PK/xCkTQXh2CaeN2AkP+SvQTznPhwBXuIyrtsR0Ua8UpmGEXI+my3qQ6o003r8gPUY2e785ywIyhX2zCA==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.17",
"vue": "^3.0.11"
}
},
"node_modules/@highlightjs/vue-plugin": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz",
@ -4997,6 +5046,15 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true
},
"node_modules/preact": {
"version": "10.12.1",
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.12.1.tgz",
"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",

7
admin/package.json

@ -10,6 +10,11 @@
},
"dependencies": {
"@element-plus/icons-vue": "2.0.10",
"@fullcalendar/core": "^6.1.17",
"@fullcalendar/daygrid": "^6.1.17",
"@fullcalendar/interaction": "^6.1.17",
"@fullcalendar/timegrid": "^6.1.17",
"@fullcalendar/vue3": "^6.1.17",
"@highlightjs/vue-plugin": "2.1.0",
"@types/lodash-es": "4.17.6",
"@vueuse/core": "9.12.0",
@ -55,4 +60,4 @@
"vite": "4.1.0",
"vue-tsc": "1.0.24"
}
}
}

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

@ -1,280 +1,282 @@
<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>
<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 } from '@/app/api/classroom'
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>

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

@ -1,287 +1,290 @@
<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>
<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 } 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>

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

@ -0,0 +1,127 @@
<template>
<div class="schedule-container">
<!-- 周视图日历 -->
<FullCalendar
:options="calendarOptions"
ref="fullCalendar"
class="calendar"
/>
<!-- 课程详情弹窗 -->
<el-dialog
v-model="dialogVisible"
title="课程人员安排"
width="30%"
:before-close="handleClose"
>
<div v-if="selectedEvent">
<h3>{{ selectedEvent.title }}</h3>
<p>时间{{ selectedEvent.time }}</p>
<h4>参与人员</h4>
<ul>
<li v-for="person in selectedEvent.people" :key="person.id">
{{ person.name }} - {{ person.role }}
</li>
</ul>
</div>
</el-dialog>
</div>
</template>
<script setup>
import {ref} from 'vue';
import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction'; //
import zhLocale from '@fullcalendar/core/locales/zh-cn';
//
const courses = ref([
{
id: 1,
title: '数学课',
start: '2025-05-18 08:00:00',
end: '2025-05-18 09:00:00',
people: [
{id: 1, name: '张老师', role: '主讲'},
{id: 2, name: '李同学', role: '学生'}
]
},
{
id: 2,
title: '英语课',
start: '2025-05-18 10:00:00',
end: '2025-05-18 11:00:00',
people: [
{id: 3, name: '王老师', role: '主讲'}
]
}
]);
//
const dialogVisible = ref(false);
const selectedEvent = ref(null);
// FullCalendar
const calendarOptions = ref({
locale: zhLocale,
plugins: [
dayGridPlugin,
timeGridPlugin,
interactionPlugin //
],
initialView: 'timeGridWeek', //
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'timeGridWeek,timeGridDay' //
},
events: courses.value,
eventClick: (info) => {
selectedEvent.value = {
title: info.event.title,
time: `${info.event.start.toLocaleTimeString()} - ${info.event.end.toLocaleTimeString()}`,
people: info.event.extendedProps.people
};
dialogVisible.value = true;
},
eventContent: (arg) => {
//
return {html: `<div class="fc-event-title">${arg.event.title}</div>`};
},
allDaySlot: false, //
slotDuration: '01:00', // 1
slotLabelFormat: {
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: false,
meridiem: 'short'
}
});
//
const handleClose = () => {
dialogVisible.value = false;
selectedEvent.value = null;
};
</script>
<style>
.schedule-container {
padding: 20px;
}
.calendar {
margin-bottom: 20px;
}
.fc-event-title {
white-space: normal !important;
font-size: 14px;
}
.fc-timegrid-event {
cursor: pointer;
}
</style>

81
admin/src/app/views/yjpz_config/yjpz_config.vue

@ -1,49 +1,54 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-form
class="page-form"
:model="formData"
label-width="150px"
ref="ruleFormRef"
:rules="formRules"
v-loading="loading"
>
<el-form-item
v-for="day in weekDays"
:key="day.key"
:label="day.label"
:prop="`priceRules.${day.key}`"
<div class="flex justify-between items-center">
<span class="text-lg">市场人员绩效配置</span>
</div>
<div style="margin-top: 20px">
<el-form
class="page-form"
:model="formData"
label-width="150px"
ref="ruleFormRef"
:rules="formRules"
v-loading="loading"
>
<span style="margin: 0 8px">每个</span>
<el-input
v-model.number="formData.priceRules[day.key].basePrice"
placeholder="请输入"
class="input-width"
clearable
/>
<span style="margin: 0 8px"> 超过</span>
<el-input
v-model.number="formData.priceRules[day.key].limitCount"
placeholder="请输入"
class="input-width"
clearable
/>
<span style="margin: 0 8px"></span>
<el-input
v-model.number="formData.priceRules[day.key].extraPrice"
placeholder="请输入"
class="input-width"
clearable
/>
<span style="margin-left: 4px"></span>
</el-form-item>
</el-form>
<el-form-item
v-for="day in weekDays"
:key="day.key"
:label="day.label"
:prop="`priceRules.${day.key}`"
>
<span style="margin: 0 8px">每个</span>
<el-input
v-model.number="formData.priceRules[day.key].basePrice"
placeholder="请输入"
class="input-width"
clearable
/>
<span style="margin: 0 8px"> 超过</span>
<el-input
v-model.number="formData.priceRules[day.key].limitCount"
placeholder="请输入"
class="input-width"
clearable
/>
<span style="margin: 0 8px"></span>
<el-input
v-model.number="formData.priceRules[day.key].extraPrice"
placeholder="请输入"
class="input-width"
clearable
/>
<span style="margin-left: 4px"></span>
</el-form-item>
</el-form>
</div>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave()">提交</el-button>
<el-button type="primary" @click="onSave()">保存</el-button>
</div>
</div>
</div>

7620
admin/yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save