修改主页工作台布局以及数据显示, 改成预报名报表统计页面
This commit is contained in:
parent
737c7a1ff1
commit
c3a8f590e8
|
|
@ -6,8 +6,8 @@ export function getConfig() {
|
|||
}
|
||||
|
||||
// 工作台主页
|
||||
export function getWorkbench() {
|
||||
return request.get({ url: '/workbench/index' })
|
||||
export function getWorkbench(params?: Record<string, any>) {
|
||||
return request.get({ url: '/workbench/index', params })
|
||||
}
|
||||
|
||||
//字典数据
|
||||
|
|
|
|||
|
|
@ -12,20 +12,10 @@
|
|||
<el-form-item label="性别" prop="gender">
|
||||
<el-select v-model="queryParams.gender" class="w-[280px]" clearable>
|
||||
<el-option label="男" value="1" />
|
||||
<el-option label="女" value="2" />
|
||||
<el-option label="女" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="身份证号" prop="idCard">
|
||||
<el-input class="w-[280px]" v-model="queryParams.idCard" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="毕业学校" prop="previousSchool">
|
||||
<el-input class="w-[280px]" v-model="queryParams.previousSchool" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="报名专业" prop="majorId">
|
||||
<el-select
|
||||
|
|
@ -63,21 +53,11 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="接待老师" prop="receptionTeacherId">
|
||||
<el-select
|
||||
v-model="queryParams.receptionTeacherId"
|
||||
class="w-[280px]"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择接待老师"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in teacherOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
<el-form-item label="创建时间">
|
||||
<daterange-picker
|
||||
v-model:startTime="queryParams.createTimeStart"
|
||||
v-model:endTime="queryParams.createTimeEnd"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
|
|
@ -90,7 +70,19 @@
|
|||
</el-form>
|
||||
</el-card>
|
||||
<el-card class="!border-none mt-4" shadow="never">
|
||||
<el-table class="mt-4" size="large" v-loading="pager.loading" :data="pager.lists">
|
||||
<div class="flex justify-start mb-2">
|
||||
<el-button type="success" plain @click="handleBatchEnroll" :disabled="!multipleSelection.length">
|
||||
批量入学
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
class="mt-4"
|
||||
size="large"
|
||||
v-loading="pager.loading"
|
||||
:data="pager.lists"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="姓名" prop="name" min-width="100" />
|
||||
<el-table-column
|
||||
label="性别"
|
||||
|
|
@ -98,71 +90,28 @@
|
|||
min-width="100"
|
||||
:formatter="formatGender"
|
||||
/>
|
||||
<el-table-column
|
||||
label="身份证号"
|
||||
prop="idCard"
|
||||
min-width="200"
|
||||
>
|
||||
<el-table-column label="身份证号" prop="idCard" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<span style="white-space: nowrap;">{{ row.idCard }}</span>
|
||||
<span style="white-space: nowrap">{{ row.idCard }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="毕业学校"
|
||||
prop="previousSchool"
|
||||
min-width="150"
|
||||
/>
|
||||
<el-table-column
|
||||
label="身高(cm)"
|
||||
prop="height"
|
||||
min-width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
label="体重(kg)"
|
||||
prop="weight"
|
||||
min-width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
label="鞋码"
|
||||
prop="shoeSize"
|
||||
min-width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
label="中考成绩"
|
||||
prop="highSchoolScore"
|
||||
min-width="120"
|
||||
/>
|
||||
<el-table-column label="毕业学校" prop="previousSchool" min-width="150" />
|
||||
<el-table-column label="报名专业" prop="majorName" min-width="120" />
|
||||
<el-table-column
|
||||
label="招生老师"
|
||||
prop="recruitmentTeacherId"
|
||||
prop="recruitmentTeacherName"
|
||||
min-width="120"
|
||||
:formatter="formatRecruitmentTeacher"
|
||||
/>
|
||||
<el-table-column
|
||||
label="接待老师"
|
||||
prop="receptionTeacherId"
|
||||
min-width="120"
|
||||
:formatter="formatReceptionTeacher"
|
||||
/>
|
||||
<el-table-column
|
||||
label="预报名金额"
|
||||
prop="preRegistrationAmount"
|
||||
min-width="120"
|
||||
/>
|
||||
<el-table-column label="创建时间" prop="createTime" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<span style="white-space: nowrap;">{{ row.createTime }}</span>
|
||||
<span style="white-space: nowrap">{{ row.createTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" prop="updateTime" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<span style="white-space: nowrap;">{{ row.updateTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="handleView(row)">详情</el-button>
|
||||
<el-button type="success" link @click="handleEnroll(row)">入学</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -174,115 +123,50 @@
|
|||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="info">
|
||||
import { infoLists } from '@/api/info/info'
|
||||
import { infoLists, infoDetail, infoEdit } from '@/api/info/info'
|
||||
import { majorLists } from '@/api/major'
|
||||
import { teacherLists } from '@/api/teacher'
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import feedback from '@/utils/feedback'
|
||||
import { snakeToCamel } from '@/utils/format'
|
||||
import request from '@/utils/request'
|
||||
|
||||
import EditPopup from '../student/info/edit.vue'
|
||||
|
||||
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
|
||||
const showEdit = ref(false)
|
||||
const multipleSelection = ref<any[]>([])
|
||||
const majorOptions = ref<Array<{ id: number; majorName: string }>>([])
|
||||
const teacherOptions = ref<Array<{ id: number; name: string }>>([])
|
||||
const queryParams = reactive({
|
||||
id: null,
|
||||
userId: null,
|
||||
studentNumber: '',
|
||||
name: '',
|
||||
gender: '',
|
||||
college: '',
|
||||
majorId: undefined as number | undefined,
|
||||
className: '',
|
||||
grade: '',
|
||||
enrollmentYear: '',
|
||||
expectedGraduationYear: '',
|
||||
studentStatus: 0,
|
||||
idCard: '',
|
||||
birthday: '',
|
||||
politicalStatus: '',
|
||||
nativePlace: '',
|
||||
homeAddress: '',
|
||||
emergencyContact: '',
|
||||
emergencyPhone: '',
|
||||
dormitory: '',
|
||||
counselorId: '',
|
||||
totalCredits: '',
|
||||
gpa: '',
|
||||
scholarshipLevel: '',
|
||||
academicWarnings: '',
|
||||
isVerified: '',
|
||||
verifiedBy: '',
|
||||
verifiedTime: '',
|
||||
previousSchool: '',
|
||||
recruitmentTeacherId: undefined as number | undefined,
|
||||
receptionTeacherId: undefined as number | undefined
|
||||
// 招生老师查询条件默认不选中任何老师,显示占位符
|
||||
recruitmentTeacherId: '' as unknown as number | undefined,
|
||||
createTimeStart: '',
|
||||
createTimeEnd: ''
|
||||
})
|
||||
const formatGender = (row: any, column: any, cellValue: any) => {
|
||||
const statusMap: Record<string | number, string> = {
|
||||
'1': '男',
|
||||
'2': '女'
|
||||
}
|
||||
return statusMap[String(cellValue)] || cellValue
|
||||
}
|
||||
|
||||
const formatStudentStatus = (row: any, column: any, cellValue: any) => {
|
||||
const statusMap: Record<string | number, string> = {
|
||||
'1': '在读',
|
||||
'2': '休学',
|
||||
'3': '毕业',
|
||||
'4': '退学'
|
||||
}
|
||||
return statusMap[String(cellValue)] || cellValue
|
||||
}
|
||||
|
||||
const formatPoliticalStatus = (row: any, column: any, cellValue: any) => {
|
||||
const statusMap: Record<string | number, string> = {
|
||||
'1': '党员',
|
||||
'2': '团员',
|
||||
'3': '群众'
|
||||
}
|
||||
return statusMap[String(cellValue)] || cellValue
|
||||
}
|
||||
|
||||
const formatScholarshipLevel = (row: any, column: any, cellValue: any) => {
|
||||
const statusMap: Record<string | number, string> = {
|
||||
'0': ' ',
|
||||
'1': '一等奖',
|
||||
'2': '二等奖',
|
||||
'3': '三等奖'
|
||||
}
|
||||
return statusMap[String(cellValue)] || cellValue
|
||||
}
|
||||
|
||||
const formatAcademicWarnings = (row: any, column: any, cellValue: any) => {
|
||||
const statusMap: Record<string | number, string> = {
|
||||
'0': ' ',
|
||||
'1': '一级预警',
|
||||
'2': '二级预警',
|
||||
'3': '三级预警'
|
||||
}
|
||||
return statusMap[String(cellValue)] || cellValue
|
||||
}
|
||||
|
||||
const formatIsVerified = (row: any, column: any, cellValue: any) => {
|
||||
const statusMap: Record<string | number, string> = {
|
||||
'0': '未认证',
|
||||
'1': '已认证'
|
||||
'0': '女'
|
||||
}
|
||||
return statusMap[String(cellValue)] || cellValue
|
||||
}
|
||||
|
||||
const formatRecruitmentTeacher = (row: any, column: any, cellValue: any) => {
|
||||
if (!cellValue) return ''
|
||||
const teacher = teacherOptions.value.find((t) => t.id === cellValue)
|
||||
return teacher ? teacher.name : cellValue
|
||||
// 优先使用后端返回的招生老师姓名
|
||||
if (row.recruitmentTeacherName != null && row.recruitmentTeacherName !== '') {
|
||||
return row.recruitmentTeacherName
|
||||
}
|
||||
|
||||
const formatReceptionTeacher = (row: any, column: any, cellValue: any) => {
|
||||
if (!cellValue) return ''
|
||||
const teacher = teacherOptions.value.find((t) => t.id === cellValue)
|
||||
return teacher ? teacher.name : cellValue
|
||||
const teacherId = row.recruitmentTeacherId
|
||||
if (teacherId == null || teacherId === '') return ''
|
||||
const teacher = teacherOptions.value.find(
|
||||
(t) => t.id == teacherId || String(t.id) === String(teacherId)
|
||||
)
|
||||
return teacher ? teacher.name : ''
|
||||
}
|
||||
|
||||
const fetchMajorOptions = async () => {
|
||||
|
|
@ -303,6 +187,10 @@ const fetchTeacherOptions = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: any[]) => {
|
||||
multipleSelection.value = val || []
|
||||
}
|
||||
|
||||
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||
fetchFun: async (params: any) => {
|
||||
// 处理参数,确保数字类型正确,并过滤空字符串
|
||||
|
|
@ -314,24 +202,26 @@ const { pager, getLists, resetPage, resetParams } = usePaging({
|
|||
}
|
||||
|
||||
// 处理数字类型字段,将空字符串或 undefined 转为 null(后端需要 null 而不是 undefined)
|
||||
if (processedParams.majorId === '' || processedParams.majorId === null || processedParams.majorId === undefined) {
|
||||
if (
|
||||
processedParams.majorId === '' ||
|
||||
processedParams.majorId === null ||
|
||||
processedParams.majorId === undefined
|
||||
) {
|
||||
processedParams.majorId = null
|
||||
} else if (typeof processedParams.majorId === 'string') {
|
||||
processedParams.majorId = parseInt(processedParams.majorId)
|
||||
}
|
||||
|
||||
if (processedParams.recruitmentTeacherId === '' || processedParams.recruitmentTeacherId === null || processedParams.recruitmentTeacherId === undefined) {
|
||||
if (
|
||||
processedParams.recruitmentTeacherId === '' ||
|
||||
processedParams.recruitmentTeacherId === null ||
|
||||
processedParams.recruitmentTeacherId === undefined
|
||||
) {
|
||||
processedParams.recruitmentTeacherId = null
|
||||
} else if (typeof processedParams.recruitmentTeacherId === 'string') {
|
||||
processedParams.recruitmentTeacherId = parseInt(processedParams.recruitmentTeacherId)
|
||||
}
|
||||
|
||||
if (processedParams.receptionTeacherId === '' || processedParams.receptionTeacherId === null || processedParams.receptionTeacherId === undefined) {
|
||||
processedParams.receptionTeacherId = null
|
||||
} else if (typeof processedParams.receptionTeacherId === 'string') {
|
||||
processedParams.receptionTeacherId = parseInt(processedParams.receptionTeacherId)
|
||||
}
|
||||
|
||||
return infoLists(processedParams)
|
||||
},
|
||||
params: queryParams
|
||||
|
|
@ -349,5 +239,43 @@ const handleView = async (data: any) => {
|
|||
editRef.value?.getDetail(data)
|
||||
}
|
||||
|
||||
const handleEnroll = async (row: any) => {
|
||||
await feedback.confirm('是否确认入学?')
|
||||
// 获取当前学生完整信息,保证必填字段齐全
|
||||
const detail = await infoDetail({
|
||||
id: row.studentId || row.student_id || row.id
|
||||
})
|
||||
const data = snakeToCamel(detail)
|
||||
// 确保主键字段存在
|
||||
if (!data.studentId) {
|
||||
data.studentId = row.studentId || row.student_id || row.id
|
||||
}
|
||||
data.id = data.studentId
|
||||
// 设置学生状态为在读
|
||||
data.studentStatus = 2
|
||||
await infoEdit(data)
|
||||
feedback.msgSuccess('入学成功')
|
||||
getLists()
|
||||
}
|
||||
|
||||
const handleBatchEnroll = async () => {
|
||||
if (!multipleSelection.value.length) return
|
||||
await feedback.confirm('是否确认批量入学?')
|
||||
const ids = multipleSelection.value
|
||||
.map((item) => item.studentId || item.student_id || item.id)
|
||||
.filter((id) => id != null)
|
||||
if (!ids.length) {
|
||||
feedback.msgError('未获取到有效的学生ID')
|
||||
return
|
||||
}
|
||||
// 批量入学接口:GET /info/batchUpdateStudentStatus?studentIdList=1&studentIdList=2...
|
||||
const query = ids.map((id) => `studentIdList=${encodeURIComponent(id)}`).join('&')
|
||||
await request.get({
|
||||
url: `/info/batchUpdateStudentStatus?${query}`
|
||||
})
|
||||
feedback.msgSuccess('批量入学成功')
|
||||
getLists()
|
||||
}
|
||||
|
||||
getLists()
|
||||
</script>
|
||||
|
|
@ -497,7 +497,7 @@
|
|||
v-model="studentFormData.preRegistrationTime"
|
||||
type="datetime"
|
||||
placeholder="请选择预报名时间"
|
||||
value-format="X"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
class="w-full"
|
||||
:disabled="isViewMode"
|
||||
/>
|
||||
|
|
@ -717,7 +717,7 @@ const studentFormData = reactive({
|
|||
preRegistrationAmount: '',
|
||||
recruitmentTeacherId: undefined as number | undefined,
|
||||
receptionTeacherId: undefined as number | undefined,
|
||||
preRegistrationTime: undefined as number | undefined,
|
||||
preRegistrationTime: undefined as string | undefined,
|
||||
invitationCode: '',
|
||||
isVerified: undefined as number | undefined,
|
||||
verifiedBy: '',
|
||||
|
|
|
|||
|
|
@ -43,59 +43,83 @@
|
|||
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-1/2 md:w-1/4">
|
||||
<div class="leading-10">销售额</div>
|
||||
<div class="text-6xl">{{ workbenchData.today.today_sales }}</div>
|
||||
<div class="leading-10">总招生数量</div>
|
||||
<div class="text-6xl">{{ workbenchData.today.total_enroll_count || 0 }}</div>
|
||||
<div class="text-tx-secondary text-xs">
|
||||
总:{{ workbenchData.today.total_sales }}
|
||||
学生状态为"预报名"或"报名"的学生总数
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2 md:w-1/4">
|
||||
<div class="leading-10">成交订单</div>
|
||||
<div class="text-6xl">{{ workbenchData.today.order_num }}</div>
|
||||
<div class="leading-10">今日招生数量</div>
|
||||
<div class="text-6xl">{{ workbenchData.today.today_enroll_count || 0 }}</div>
|
||||
<div class="text-tx-secondary text-xs">
|
||||
总:{{ workbenchData.today.order_sum }}
|
||||
今天新增的招生人数
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2 md:w-1/4">
|
||||
<div class="leading-10">新增用户</div>
|
||||
<div class="text-6xl">{{ workbenchData.today.today_new_user }}</div>
|
||||
<div class="leading-10">本周招生数量</div>
|
||||
<div class="text-6xl">{{ workbenchData.today.week_enroll_count || 0 }}</div>
|
||||
<div class="text-tx-secondary text-xs">
|
||||
总:{{ workbenchData.today.total_new_user }}
|
||||
最近 7 天的招生人数
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2 md:w-1/4">
|
||||
<div class="leading-10">新增访问量</div>
|
||||
<div class="text-6xl">{{ workbenchData.today.today_visitor }}</div>
|
||||
<div class="leading-10">本月招生数量</div>
|
||||
<div class="text-6xl">{{ workbenchData.today.month_enroll_count || 0 }}</div>
|
||||
<div class="text-tx-secondary text-xs">
|
||||
总:{{ workbenchData.today.total_visitor }}
|
||||
最近 30 天的招生人数
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="function mb-4">
|
||||
<el-card class="flex-1 !border-none" shadow="never">
|
||||
<template #header>
|
||||
<span>常用功能</span>
|
||||
</template>
|
||||
<div class="flex flex-wrap">
|
||||
<div
|
||||
v-for="item in workbenchData.menu"
|
||||
class="md:w-[12.5%] w-1/4 flex flex-col items-center"
|
||||
:key="item"
|
||||
>
|
||||
<router-link :to="item.url" class="mb-3 flex flex-col items-center">
|
||||
<image-contain width="40px" height="40px" :src="item?.image" />
|
||||
<div class="mt-2">{{ item.name }}</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<!-- <div class="function mb-4">-->
|
||||
<!-- <el-card class="flex-1 !border-none" shadow="never">-->
|
||||
<!-- <template #header>-->
|
||||
<!-- <span>常用功能</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- <div class="flex flex-wrap">-->
|
||||
<!-- <div-->
|
||||
<!-- v-for="item in workbenchData.menu"-->
|
||||
<!-- class="md:w-[12.5%] w-1/4 flex flex-col items-center"-->
|
||||
<!-- :key="item"-->
|
||||
<!-- >-->
|
||||
<!-- <router-link :to="item.url" class="mb-3 flex flex-col items-center">-->
|
||||
<!-- <image-contain width="40px" height="40px" :src="item?.image" />-->
|
||||
<!-- <div class="mt-2">{{ item.name }}</div>-->
|
||||
<!-- </router-link>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-card>-->
|
||||
<!-- </div>-->
|
||||
<div class="lg:flex gap-4">
|
||||
<el-card class="!border-none w-full lg:w-3/3" shadow="never">
|
||||
<template #header>
|
||||
<span>访问量趋势图</span>
|
||||
<div class="flex justify-between items-center">
|
||||
<span>招生趋势图</span>
|
||||
<div class="flex items-center space-x-4">
|
||||
<el-select
|
||||
v-model="filterForm.teacherId"
|
||||
class="w-[220px]"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择招生老师"
|
||||
@change="handleFilterChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in teacherOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-radio-group v-model="filterForm.rangeType" @change="handleFilterChange">
|
||||
<el-radio-button label="day">日</el-radio-button>
|
||||
<el-radio-button label="week">周</el-radio-button>
|
||||
<el-radio-button label="month">月</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<v-charts
|
||||
|
|
@ -125,6 +149,8 @@
|
|||
import vCharts from 'vue-echarts'
|
||||
|
||||
import { getWorkbench } from '@/api/app'
|
||||
import { teacherLists } from '@/api/teacher'
|
||||
|
||||
// 表单数据
|
||||
const workbenchData: any = reactive({
|
||||
version: {
|
||||
|
|
@ -137,9 +163,9 @@ const workbenchData: any = reactive({
|
|||
}
|
||||
},
|
||||
support: [],
|
||||
today: {}, // 今日数据
|
||||
today: {}, // 今日/统计数据
|
||||
menu: [], // 常用功能
|
||||
visitor: [], // 访问量
|
||||
visitor: [], // 招生趋势原始数据
|
||||
article: [], // 文章阅读量
|
||||
|
||||
visitorOption: {
|
||||
|
|
@ -148,151 +174,81 @@ const workbenchData: any = reactive({
|
|||
data: []
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
type: 'value',
|
||||
name: '单位(人)'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['访问量']
|
||||
},
|
||||
itemStyle: {
|
||||
// 点的颜色。
|
||||
color: 'red'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '访问量',
|
||||
data: [],
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: '#4A5DFF',
|
||||
width: 2
|
||||
},
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#4A5DFF'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#5777ff'
|
||||
}
|
||||
]
|
||||
},
|
||||
opacity: 0.1
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
saleOption: {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: []
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '单位(万)'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [],
|
||||
type: 'bar',
|
||||
showBackground: true,
|
||||
backgroundStyle: {
|
||||
color: 'rgba(180, 180, 180, 0.2)',
|
||||
borderRadius: [10, 10, 0, 0]
|
||||
},
|
||||
barWidth: '40%',
|
||||
itemStyle: {
|
||||
borderRadius: [10, 10, 0, 0],
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#4A5DFF'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#5777ff'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
series: []
|
||||
}
|
||||
})
|
||||
|
||||
// 招生趋势筛选条件
|
||||
const filterForm = reactive<{
|
||||
teacherId: number | null
|
||||
rangeType: 'day' | 'week' | 'month'
|
||||
}>({
|
||||
teacherId: null,
|
||||
rangeType: 'week'
|
||||
})
|
||||
|
||||
// 招生老师下拉数据
|
||||
const teacherOptions = ref<Array<{ id: number; name: string }>>([])
|
||||
|
||||
const fetchTeacherOptions = async () => {
|
||||
try {
|
||||
const res: any = await teacherLists()
|
||||
teacherOptions.value = res.lists || []
|
||||
} catch (e) {
|
||||
// 静默处理,工作台不因下拉失败报错中断
|
||||
console.error('获取招生老师列表失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取工作台主页数据
|
||||
const getData = () => {
|
||||
getWorkbench()
|
||||
const params: Record<string, any> = {
|
||||
rangeType: filterForm.rangeType
|
||||
}
|
||||
if (filterForm.teacherId) {
|
||||
params.teacherId = filterForm.teacherId
|
||||
}
|
||||
|
||||
getWorkbench(params)
|
||||
.then((res: any) => {
|
||||
workbenchData.version = res.version
|
||||
workbenchData.today = res.today
|
||||
workbenchData.menu = res.menu
|
||||
workbenchData.visitor = res.visitor
|
||||
workbenchData.today = res.today || {}
|
||||
workbenchData.menu = res.menu || []
|
||||
workbenchData.visitor = res.enrollmentTrend || {}
|
||||
workbenchData.support = res.support
|
||||
|
||||
// 清空echarts 数据
|
||||
workbenchData.visitorOption.xAxis.data = []
|
||||
workbenchData.visitorOption.series[0].data = []
|
||||
workbenchData.saleOption.xAxis.data = []
|
||||
workbenchData.saleOption.series[0].data = []
|
||||
const trend = res.enrollmentTrend || {}
|
||||
const dates: string[] = Array.isArray(trend.date) ? trend.date : []
|
||||
const list: any[] = Array.isArray(trend.list) ? trend.list : []
|
||||
|
||||
// 写入从后台拿来的数据
|
||||
res.visitor.date.reverse().forEach((item: any) => {
|
||||
workbenchData.visitorOption.xAxis.data.push(item)
|
||||
// 清空 ECharts 数据
|
||||
workbenchData.visitorOption.xAxis.data = []
|
||||
workbenchData.visitorOption.series = []
|
||||
workbenchData.visitorOption.legend.data = []
|
||||
|
||||
// 写入从后台拿来的数据:每个老师一条线
|
||||
workbenchData.visitorOption.xAxis.data = dates
|
||||
|
||||
list.forEach((item: any) => {
|
||||
const name = item.name || '未知老师'
|
||||
const data = Array.isArray(item.data) ? item.data : []
|
||||
|
||||
workbenchData.visitorOption.legend.data.push(name)
|
||||
workbenchData.visitorOption.series.push({
|
||||
name,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data
|
||||
})
|
||||
res.visitor.list[0].data.forEach((item: any) => {
|
||||
workbenchData.visitorOption.series[0].data.push(item)
|
||||
})
|
||||
res.sale.date.reverse().forEach((item: any) => {
|
||||
workbenchData.saleOption.xAxis.data.push(item)
|
||||
})
|
||||
res.sale.list[0].data.forEach((item: any) => {
|
||||
if (item <= 50) {
|
||||
item = {
|
||||
value: item,
|
||||
itemStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#ff8729'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#ff8729'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
workbenchData.saleOption.series[0].data.push(item)
|
||||
})
|
||||
})
|
||||
.catch((err: any) => {
|
||||
|
|
@ -300,7 +256,12 @@ const getData = () => {
|
|||
})
|
||||
}
|
||||
|
||||
const handleFilterChange = () => {
|
||||
getData()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchTeacherOptions()
|
||||
getData()
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import io.swagger.annotations.ApiOperation;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
|
@ -23,8 +24,10 @@ public class IndexController {
|
|||
|
||||
@GetMapping("/index")
|
||||
@ApiOperation(value="控制台")
|
||||
public AjaxResult<Map<String, Object>> index() {
|
||||
Map<String, Object> map = iIndexService.index();
|
||||
public AjaxResult<Map<String, Object>> index(
|
||||
@RequestParam(value = "teacherId", required = false) Integer teacherId,
|
||||
@RequestParam(value = "rangeType", required = false) String rangeType) {
|
||||
Map<String, Object> map = iIndexService.index(teacherId, rangeType);
|
||||
return AjaxResult.success(map);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import org.springframework.validation.annotation.Validated;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("adminapi/info")
|
||||
|
|
@ -58,6 +59,14 @@ public class StudentInfoController {
|
|||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@Log(title = "学生批量入学")
|
||||
@GetMapping("/batchUpdateStudentStatus")
|
||||
@ApiOperation(value = "学生批量入学")
|
||||
public AjaxResult<Object> batchUpdateStudentStatus(@RequestParam("studentIdList") List<Integer> studentIdList) {
|
||||
iStudentInfoService.batchUpdateStudentStatus(studentIdList);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@Log(title = "学生信息删除")
|
||||
@PostMapping("/del")
|
||||
@ApiOperation(value = "学生信息删除")
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ public interface IIndexService {
|
|||
* 控制台数据
|
||||
*
|
||||
* @author fzr
|
||||
* @param teacherId 招生老师ID(可选)
|
||||
* @param rangeType 时间区间类型:day / week / month(可选,默认 week)
|
||||
* @return Map<String, Object>
|
||||
*/
|
||||
Map<String, Object> index();
|
||||
Map<String, Object> index(Integer teacherId, String rangeType);
|
||||
|
||||
/**
|
||||
* 公共配置
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.mdd.admin.vo.StudentInfoDetailVo;
|
|||
import com.mdd.common.core.PageResult;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 学生信息服务接口类
|
||||
|
|
@ -58,4 +59,6 @@ public interface IStudentInfoService {
|
|||
* @param id 主键ID
|
||||
*/
|
||||
void del(Integer id);
|
||||
|
||||
void batchUpdateStudentStatus(List<Integer> studentIdList);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,21 @@ import com.alibaba.fastjson2.JSONArray;
|
|||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.mdd.admin.service.IIndexService;
|
||||
import com.mdd.common.config.GlobalConfig;
|
||||
import com.mdd.common.util.*;
|
||||
import com.mdd.common.entity.StudentInfo;
|
||||
import com.mdd.common.entity.Teacher;
|
||||
import com.mdd.common.mapper.StudentInfoMapper;
|
||||
import com.mdd.common.mapper.TeacherMapper;
|
||||
import com.mdd.common.util.ConfigUtils;
|
||||
import com.mdd.common.util.ListUtils;
|
||||
import com.mdd.common.util.TimeUtils;
|
||||
import com.mdd.common.util.UrlUtils;
|
||||
import com.mdd.common.util.YmlUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
|
|
@ -15,14 +27,22 @@ import java.util.*;
|
|||
@Service
|
||||
public class IndexServiceImpl implements IIndexService {
|
||||
|
||||
@Resource
|
||||
private StudentInfoMapper studentInfoMapper;
|
||||
|
||||
@Resource
|
||||
private TeacherMapper teacherMapper;
|
||||
|
||||
/**
|
||||
* 控制台数据
|
||||
*
|
||||
* @author fzr
|
||||
* @param teacherId 招生老师ID(可选)
|
||||
* @param rangeType 时间区间类型:day / week / month(可选,默认 week)
|
||||
* @return Map<String, Object>
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> index() {
|
||||
public Map<String, Object> index(Integer teacherId, String rangeType) {
|
||||
Map<String, Object> console = new LinkedHashMap<>();
|
||||
|
||||
// 版本信息
|
||||
|
|
@ -37,30 +57,15 @@ public class IndexServiceImpl implements IIndexService {
|
|||
version.put("channel", channel);
|
||||
console.put("version", version);
|
||||
|
||||
// 今日数据
|
||||
Map<String, Object> today = new LinkedHashMap<>();
|
||||
today.put("time", "2022-08-11 15:08:29");
|
||||
today.put("today_visitor", 10); // 访问量(人)
|
||||
today.put("total_visitor", 100); // 总访问量
|
||||
today.put("today_sales", 30); // 销售额(元)
|
||||
today.put("total_sales", 65); // 总销售额
|
||||
today.put("order_num", 12); // 订单量(笔)
|
||||
today.put("order_sum", 255); // 总订单量
|
||||
today.put("today_new_user", 120); // 新增用户
|
||||
today.put("total_new_user", 360); // 总访用户
|
||||
console.put("today", today);
|
||||
// 招生统计数据
|
||||
Map<String, Object> enrollmentStats = buildEnrollmentStats();
|
||||
console.put("today", enrollmentStats);
|
||||
|
||||
// 访客图表
|
||||
Map<String, Object> visitor = new LinkedHashMap<>();
|
||||
visitor.put("date", TimeUtils.daysAgoDate(15));
|
||||
visitor.put("list", new JSONArray() {{
|
||||
add(new JSONObject() {{
|
||||
put("name", "访客数");
|
||||
put("data", Arrays.asList(12,13,11,5,8,22,14,9,456,62,78,12,18,22,46));
|
||||
}});
|
||||
}});
|
||||
console.put("visitor", visitor);
|
||||
// 招生趋势数据(带筛选条件)
|
||||
Map<String, Object> enrollmentTrend = buildEnrollmentTrend(teacherId, rangeType);
|
||||
console.put("enrollmentTrend", enrollmentTrend);
|
||||
|
||||
// 常用功能菜单
|
||||
console.put("menu", new JSONArray() {{
|
||||
|
||||
add(new JSONObject() {{
|
||||
|
|
@ -113,10 +118,190 @@ public class IndexServiceImpl implements IIndexService {
|
|||
|
||||
}});
|
||||
|
||||
|
||||
return console;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建招生统计数据:
|
||||
* - 总招生数量
|
||||
* - 今日招生数量
|
||||
* - 本周招生数量
|
||||
* - 本月招生数量
|
||||
*
|
||||
* 招生数量定义:student_status = 0 或 1 的学生数量
|
||||
*/
|
||||
private Map<String, Object> buildEnrollmentStats() {
|
||||
Map<String, Object> stats = new LinkedHashMap<>();
|
||||
|
||||
// 当前时间
|
||||
Date now = new Date();
|
||||
stats.put("time", new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(now));
|
||||
|
||||
// 总招生数量(所有时间)
|
||||
long totalCount = studentInfoMapper.selectCount(
|
||||
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<StudentInfo>()
|
||||
.in("student_status", 0, 1)
|
||||
.isNull("delete_time")
|
||||
);
|
||||
|
||||
// 今日、本周、本月时间范围
|
||||
LocalDate today = LocalDate.now();
|
||||
Date todayStart = toDate(today.atStartOfDay());
|
||||
Date tomorrowStart = toDate(today.plusDays(1).atStartOfDay());
|
||||
|
||||
LocalDate weekStartDate = today.minusDays(6); // 最近7天
|
||||
Date weekStart = toDate(weekStartDate.atStartOfDay());
|
||||
|
||||
LocalDate monthStartDate = today.minusDays(29); // 最近30天
|
||||
Date monthStart = toDate(monthStartDate.atStartOfDay());
|
||||
|
||||
long todayCount = countEnrollmentInRange(todayStart, tomorrowStart);
|
||||
long weekCount = countEnrollmentInRange(weekStart, tomorrowStart);
|
||||
long monthCount = countEnrollmentInRange(monthStart, tomorrowStart);
|
||||
|
||||
stats.put("total_enroll_count", totalCount);
|
||||
stats.put("today_enroll_count", todayCount);
|
||||
stats.put("week_enroll_count", weekCount);
|
||||
stats.put("month_enroll_count", monthCount);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算时间区间内的招生数量(按 pre_registration_time 筛选)
|
||||
*/
|
||||
private long countEnrollmentInRange(Date startTime, Date endTime) {
|
||||
return studentInfoMapper.selectCount(
|
||||
new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<StudentInfo>()
|
||||
.in("student_status", 0, 1)
|
||||
.isNull("delete_time")
|
||||
.isNotNull("pre_registration_time")
|
||||
.ge("pre_registration_time", startTime)
|
||||
.lt("pre_registration_time", endTime)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建招生趋势数据
|
||||
*
|
||||
* @param teacherId 招生老师ID(为空则统计所有老师)
|
||||
* @param rangeType 时间区间类型:day / week / month
|
||||
*/
|
||||
private Map<String, Object> buildEnrollmentTrend(Integer teacherId, String rangeType) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
|
||||
if (rangeType == null || rangeType.trim().isEmpty()) {
|
||||
rangeType = "week";
|
||||
}
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDate startDate;
|
||||
|
||||
switch (rangeType) {
|
||||
case "day":
|
||||
startDate = today;
|
||||
break;
|
||||
case "month":
|
||||
startDate = today.minusDays(29);
|
||||
break;
|
||||
case "week":
|
||||
default:
|
||||
startDate = today.minusDays(6);
|
||||
break;
|
||||
}
|
||||
|
||||
LocalDate endDate = today;
|
||||
Date startTime = toDate(startDate.atStartOfDay());
|
||||
Date endTime = toDate(endDate.plusDays(1).atStartOfDay());
|
||||
|
||||
// 生成日期列表
|
||||
List<String> dateList = new ArrayList<>();
|
||||
Map<String, Integer> dateIndexMap = new LinkedHashMap<>();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
int idx = 0;
|
||||
for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
|
||||
String dateStr = d.format(formatter);
|
||||
dateList.add(dateStr);
|
||||
dateIndexMap.put(dateStr, idx++);
|
||||
}
|
||||
|
||||
// 查询数据库中的统计数据:按日期 + 招生老师分组
|
||||
List<Map<String, Object>> rows = studentInfoMapper.selectEnrollmentTrend(startTime, endTime, teacherId);
|
||||
|
||||
// 收集涉及到的老师ID
|
||||
Set<Integer> teacherIds = new LinkedHashSet<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
Object tIdObj = row.get("teacherId");
|
||||
if (tIdObj != null) {
|
||||
teacherIds.add(((Number) tIdObj).intValue());
|
||||
}
|
||||
}
|
||||
|
||||
Map<Integer, String> teacherNameMap = new LinkedHashMap<>();
|
||||
if (!teacherIds.isEmpty()) {
|
||||
List<Teacher> teachers = teacherMapper.selectBatchIds(teacherIds);
|
||||
for (Teacher t : teachers) {
|
||||
teacherNameMap.put(t.getTeacherId(), t.getTeacherName());
|
||||
}
|
||||
}
|
||||
|
||||
// 构建每个老师的时间序列数据
|
||||
Map<Integer, List<Long>> teacherSeriesMap = new LinkedHashMap<>();
|
||||
for (Integer tId : teacherIds) {
|
||||
List<Long> initList = new ArrayList<>();
|
||||
for (int i = 0; i < dateList.size(); i++) {
|
||||
initList.add(0L);
|
||||
}
|
||||
teacherSeriesMap.put(tId, initList);
|
||||
}
|
||||
|
||||
for (Map<String, Object> row : rows) {
|
||||
Object dateObj = row.get("statDate");
|
||||
Object tIdObj = row.get("teacherId");
|
||||
Object countObj = row.get("enrollCount");
|
||||
if (dateObj == null || tIdObj == null || countObj == null) {
|
||||
continue;
|
||||
}
|
||||
String dateStr = String.valueOf(dateObj);
|
||||
Integer tId = ((Number) tIdObj).intValue();
|
||||
Integer index = dateIndexMap.get(dateStr);
|
||||
if (index == null) {
|
||||
continue;
|
||||
}
|
||||
List<Long> dataList = teacherSeriesMap.get(tId);
|
||||
if (dataList == null) {
|
||||
dataList = new ArrayList<>();
|
||||
for (int i = 0; i < dateList.size(); i++) {
|
||||
dataList.add(0L);
|
||||
}
|
||||
teacherSeriesMap.put(tId, dataList);
|
||||
}
|
||||
long count = ((Number) countObj).longValue();
|
||||
dataList.set(index, count);
|
||||
}
|
||||
|
||||
// 组装返回给前端的 list 数据
|
||||
JSONArray seriesList = new JSONArray();
|
||||
for (Map.Entry<Integer, List<Long>> entry : teacherSeriesMap.entrySet()) {
|
||||
Integer tId = entry.getKey();
|
||||
List<Long> data = entry.getValue();
|
||||
JSONObject item = new JSONObject();
|
||||
item.put("teacherId", tId);
|
||||
item.put("name", teacherNameMap.getOrDefault(tId, "未知老师"));
|
||||
item.put("data", data);
|
||||
seriesList.add(item);
|
||||
}
|
||||
|
||||
result.put("date", dateList);
|
||||
result.put("list", seriesList);
|
||||
result.put("rangeType", rangeType);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Date toDate(java.time.LocalDateTime localDateTime) {
|
||||
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* 公共配置
|
||||
*
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.mdd.admin.validate.commons.PageValidate;
|
||||
import com.mdd.admin.service.IStudentInfoService;
|
||||
import com.mdd.admin.validate.StudentInfoCreateValidate;
|
||||
|
|
@ -12,18 +13,12 @@ import com.mdd.admin.validate.StudentInfoSearchValidate;
|
|||
import com.mdd.admin.vo.StudentInfoListedVo;
|
||||
import com.mdd.admin.vo.StudentInfoDetailVo;
|
||||
import com.mdd.common.core.PageResult;
|
||||
import com.mdd.common.entity.*;
|
||||
import com.mdd.common.entity.Class;
|
||||
import com.mdd.common.entity.College;
|
||||
import com.mdd.common.entity.Major;
|
||||
import com.mdd.common.entity.StudentInfo;
|
||||
import com.mdd.common.entity.StudentBaseInfo;
|
||||
import com.mdd.common.entity.admin.Admin;
|
||||
import com.mdd.common.entity.user.User;
|
||||
import com.mdd.common.mapper.ClassMapper;
|
||||
import com.mdd.common.mapper.CollegeMapper;
|
||||
import com.mdd.common.mapper.MajorMapper;
|
||||
import com.mdd.common.mapper.StudentInfoMapper;
|
||||
import com.mdd.common.mapper.StudentBaseInfoMapper;
|
||||
import com.mdd.common.exception.OperateException;
|
||||
import com.mdd.common.mapper.*;
|
||||
import com.mdd.common.mapper.admin.AdminMapper;
|
||||
import com.mdd.common.mapper.user.UserMapper;
|
||||
|
||||
|
|
@ -33,6 +28,7 @@ import java.text.SimpleDateFormat;
|
|||
|
||||
import com.mdd.common.util.*;
|
||||
import io.netty.util.internal.ThreadLocalRandom;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -47,7 +43,7 @@ import java.util.*;
|
|||
* @author gyp
|
||||
*/
|
||||
@Service
|
||||
public class StudentInfoServiceImpl implements IStudentInfoService {
|
||||
public class StudentInfoServiceImpl extends ServiceImpl<StudentInfoMapper, StudentInfo> implements IStudentInfoService {
|
||||
|
||||
@Resource
|
||||
StudentInfoMapper studentInfoMapper;
|
||||
|
|
@ -63,6 +59,8 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
|
|||
private AdminMapper adminMapper;
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
@Autowired
|
||||
private TeacherMapper teacherMapper;
|
||||
|
||||
/**
|
||||
* 学生信息列表
|
||||
|
|
@ -95,6 +93,7 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
|
|||
"=:verifiedBy@verified_by:int",
|
||||
"=:recruitmentTeacherId@recruitment_teacher_id:int",
|
||||
"=:receptionTeacherId@reception_teacher_id:int",
|
||||
"datetime:createTimeStart-createTimeEnd@create_time:str",
|
||||
});
|
||||
|
||||
IPage<StudentInfo> iPage = studentInfoMapper.selectPage(new Page<>(page, limit), queryWrapper);
|
||||
|
|
@ -133,12 +132,14 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
|
|||
Class clazz = classMapper.selectById(item.getClassId());
|
||||
Admin counselor = adminMapper.selectById(item.getCounselorId());
|
||||
Admin verifier = adminMapper.selectById(item.getVerifiedBy());
|
||||
Teacher teacher = teacherMapper.selectById(item.getRecruitmentTeacherId());
|
||||
|
||||
vo.setCollegeName(college != null ? college.getCollegeName() : "");
|
||||
vo.setMajorName(major != null ? major.getMajorName() : "");
|
||||
vo.setClassName(clazz != null ? clazz.getClassName() : "");
|
||||
vo.setCounselorName(counselor != null ? counselor.getName() : "");
|
||||
vo.setVerifierName(verifier != null ? verifier.getName() : "");
|
||||
vo.setRecruitmentTeacherName(teacher != null ? teacher.getTeacherName() : null);
|
||||
|
||||
list.add(vo);
|
||||
}
|
||||
|
|
@ -232,6 +233,10 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
|
|||
studentInfo.setIsVerified(createValidate.getIsVerified() != null ? createValidate.getIsVerified() : 0);
|
||||
studentInfo.setVerifiedBy(createValidate.getVerifiedBy());
|
||||
studentInfo.setVerifiedTime(createValidate.getVerifiedTime());
|
||||
studentInfo.setPreRegistrationTime(createValidate.getPreRegistrationTime());
|
||||
studentInfo.setRecruitmentTeacherId(createValidate.getRecruitmentTeacherId());
|
||||
studentInfo.setReceptionTeacherId(createValidate.getReceptionTeacherId());
|
||||
studentInfo.setInvitationCode(createValidate.getInvitationCode());
|
||||
studentInfoMapper.insert(studentInfo);
|
||||
|
||||
// 创建基本信息
|
||||
|
|
@ -384,6 +389,15 @@ public class StudentInfoServiceImpl implements IStudentInfoService {
|
|||
studentInfoMapper.delete(new QueryWrapper<StudentInfo>().eq("student_id", id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void batchUpdateStudentStatus(List<Integer> studentIdList) {
|
||||
if (CollectionUtils.isEmpty(studentIdList)) {
|
||||
throw new OperateException("批量入学失败, 学生idList丢失");
|
||||
}
|
||||
|
||||
studentInfoMapper.batchUpdateStudentStatus(studentIdList);
|
||||
}
|
||||
|
||||
private <T, M extends BaseMapper<T>> T getRandomEntity(M mapper) {
|
||||
try {
|
||||
QueryWrapper<T> wrapper = new QueryWrapper<>();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.mdd.admin.validate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
|
@ -75,7 +76,8 @@ public class StudentInfoCreateValidate implements Serializable {
|
|||
private Integer receptionTeacherId;
|
||||
|
||||
@ApiModelProperty(value = "预报名时间")
|
||||
private Long preRegistrationTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private java.util.Date preRegistrationTime;
|
||||
|
||||
@ApiModelProperty(value = "邀请码")
|
||||
private String invitationCode;
|
||||
|
|
|
|||
|
|
@ -56,4 +56,10 @@ public class StudentInfoSearchValidate implements Serializable {
|
|||
@ApiModelProperty(value = "接待老师ID")
|
||||
private Integer receptionTeacherId;
|
||||
|
||||
@ApiModelProperty(value = "创建时间开始")
|
||||
private String createTimeStart;
|
||||
|
||||
@ApiModelProperty(value = "创建时间结束")
|
||||
private String createTimeEnd;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.mdd.admin.validate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
|
@ -25,7 +26,7 @@ public class StudentInfoUpdateValidate implements Serializable {
|
|||
@ApiModelProperty(value = "主键")
|
||||
private Long studentId;
|
||||
|
||||
@NotNull(message = "studentNumber参数缺失")
|
||||
// @NotNull(message = "studentNumber参数缺失")
|
||||
@ApiModelProperty(value = "学号")
|
||||
private String studentNumber;
|
||||
|
||||
|
|
@ -41,19 +42,19 @@ public class StudentInfoUpdateValidate implements Serializable {
|
|||
@ApiModelProperty(value = "班级ID")
|
||||
private Integer classId;
|
||||
|
||||
@NotNull(message = "grade参数缺失")
|
||||
// @NotNull(message = "grade参数缺失")
|
||||
@ApiModelProperty(value = "年级")
|
||||
private Integer grade;
|
||||
|
||||
@NotNull(message = "enrollmentYear参数缺失")
|
||||
// @NotNull(message = "enrollmentYear参数缺失")
|
||||
@ApiModelProperty(value = "入学年份")
|
||||
private Integer enrollmentYear;
|
||||
|
||||
@NotNull(message = "expectedGraduationYear参数缺失")
|
||||
// @NotNull(message = "expectedGraduationYear参数缺失")
|
||||
@ApiModelProperty(value = "预计毕业年份")
|
||||
private Integer expectedGraduationYear;
|
||||
|
||||
@NotNull(message = "studentStatus参数缺失")
|
||||
// @NotNull(message = "studentStatus参数缺失")
|
||||
@ApiModelProperty(value = "学生状态: [0=预报名, 1=报名, 2=在读, 3=休学, 4=毕业, 5=退学]")
|
||||
private Integer studentStatus;
|
||||
|
||||
|
|
@ -85,7 +86,8 @@ public class StudentInfoUpdateValidate implements Serializable {
|
|||
private Integer receptionTeacherId;
|
||||
|
||||
@ApiModelProperty(value = "预报名时间")
|
||||
private Long preRegistrationTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private java.util.Date preRegistrationTime;
|
||||
|
||||
@ApiModelProperty(value = "邀请码")
|
||||
private String invitationCode;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.mdd.admin.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
|
@ -69,7 +70,8 @@ public class StudentInfoDetailVo implements Serializable {
|
|||
private Integer receptionTeacherId;
|
||||
|
||||
@ApiModelProperty(value = "预报名时间")
|
||||
private Long preRegistrationTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private java.util.Date preRegistrationTime;
|
||||
|
||||
@ApiModelProperty(value = "邀请码")
|
||||
private String invitationCode;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,9 @@ public class StudentInfoListedVo implements Serializable {
|
|||
@ApiModelProperty(value = "招生老师ID")
|
||||
private Integer recruitmentTeacherId;
|
||||
|
||||
@ApiModelProperty(value = "招生老师名字")
|
||||
private String recruitmentTeacherName;
|
||||
|
||||
@ApiModelProperty(value = "接待老师ID")
|
||||
private Integer receptionTeacherId;
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class StudentInfo implements Serializable {
|
|||
private Integer receptionTeacherId;
|
||||
|
||||
@ApiModelProperty(value = "预报名时间")
|
||||
private Long preRegistrationTime;
|
||||
private Date preRegistrationTime;
|
||||
|
||||
@ApiModelProperty(value = "邀请码")
|
||||
private String invitationCode;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ package com.mdd.common.mapper;
|
|||
import com.mdd.common.core.basics.IBaseMapper;
|
||||
import com.mdd.common.entity.StudentInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 学生信息Mapper
|
||||
|
|
@ -10,4 +15,18 @@ import org.apache.ibatis.annotations.Mapper;
|
|||
*/
|
||||
@Mapper
|
||||
public interface StudentInfoMapper extends IBaseMapper<StudentInfo> {
|
||||
|
||||
void batchUpdateStudentStatus(@Param("studentIdList") List<Integer> studentIdList);
|
||||
|
||||
/**
|
||||
* 按日期和招生老师统计招生数量(student_status = 0 或 1)
|
||||
*
|
||||
* @param startTime 开始时间(含)
|
||||
* @param endTime 结束时间(不含)
|
||||
* @param teacherId 招生老师ID(可选)
|
||||
* @return 每行包含 statDate(yyyy-MM-dd)、teacherId、enrollCount
|
||||
*/
|
||||
List<Map<String, Object>> selectEnrollmentTrend(@Param("startTime") Date startTime,
|
||||
@Param("endTime") Date endTime,
|
||||
@Param("teacherId") Integer teacherId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mdd.common.mapper.StudentInfoMapper">
|
||||
|
||||
<!-- 批量更新学生状态为在读(2) -->
|
||||
<update id="batchUpdateStudentStatus">
|
||||
UPDATE la_student_info
|
||||
SET student_status = 2
|
||||
WHERE student_id IN
|
||||
<foreach collection="studentIdList" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<!-- 按日期和招生老师统计招生数量(student_status = 0 或 1),时间区间按 pre_registration_time -->
|
||||
<select id="selectEnrollmentTrend" resultType="map">
|
||||
SELECT
|
||||
DATE(pre_registration_time) AS statDate,
|
||||
recruitment_teacher_id AS teacherId,
|
||||
COUNT(*) AS enrollCount
|
||||
FROM la_student_info
|
||||
WHERE delete_time IS NULL
|
||||
AND student_status IN (0, 1)
|
||||
AND pre_registration_time IS NOT NULL
|
||||
<if test="startTime != null">
|
||||
AND pre_registration_time <![CDATA[>=]]> #{startTime}
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
AND pre_registration_time <![CDATA[<]]> #{endTime}
|
||||
</if>
|
||||
<if test="teacherId != null">
|
||||
AND recruitment_teacher_id = #{teacherId}
|
||||
</if>
|
||||
GROUP BY DATE(pre_registration_time), recruitment_teacher_id
|
||||
ORDER BY statDate ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
Loading…
Reference in New Issue