学业预警自动化
This commit is contained in:
@@ -16,7 +16,6 @@ const myWarnings = ref<any[]>([])
|
||||
const loading = ref(false)
|
||||
const detecting = ref(false)
|
||||
const filterType = ref('')
|
||||
const schedule = reactive({ isEnabled: false, dayOfWeek: 1, hour: 8, minute: 0, lastRunAt: null as string | null })
|
||||
|
||||
const typeLabels: Record<number, string> = { 1: '不及格学分', 2: '低绩点', 3: '缺勤', 4: '延毕风险' }
|
||||
const statusLabels: Record<number, string> = { 1: '生效中', 2: '已确认', 3: '已处理', 4: '已忽略' }
|
||||
@@ -31,12 +30,13 @@ function thresholdUnit(type: number): string {
|
||||
return '门 累计不及格'
|
||||
}
|
||||
|
||||
// Reactive rule objects — one per type, bound directly to v-model
|
||||
const weekOptions = [{ value: 0, label: '每天' }, { value: 1, label: '周一' }, { value: 2, label: '周二' }, { value: 3, label: '周三' }, { value: 4, label: '周四' }, { value: 5, label: '周五' }, { value: 6, label: '周六' }, { value: 7, label: '周日' }]
|
||||
|
||||
const ruleRows = reactive([
|
||||
{ type: 1 as number, name: '不及格学分', threshold: 2, isEnabled: true, notifyStudent: true, notifyCounselor: true, description: '' },
|
||||
{ type: 2, name: '低绩点', threshold: 2, isEnabled: true, notifyStudent: true, notifyCounselor: true, description: '' },
|
||||
{ type: 3, name: '缺勤', threshold: 3, isEnabled: true, notifyStudent: true, notifyCounselor: true, description: '' },
|
||||
{ type: 4, name: '延毕风险', threshold: 5, isEnabled: true, notifyStudent: true, notifyCounselor: true, description: '' },
|
||||
{ type: 1 as number, name: '不及格学分', threshold: 2, isEnabled: true, notifyStudent: true, notifyCounselor: true, description: '', autoCheckEnabled: false, checkDayOfWeek: 0, checkHour: 8, checkMinute: 0, lastCheckAt: null as string | null },
|
||||
{ type: 2, name: '低绩点', threshold: 2, isEnabled: true, notifyStudent: true, notifyCounselor: true, description: '', autoCheckEnabled: false, checkDayOfWeek: 0, checkHour: 8, checkMinute: 0, lastCheckAt: null as string | null },
|
||||
{ type: 3, name: '缺勤', threshold: 3, isEnabled: true, notifyStudent: true, notifyCounselor: true, description: '', autoCheckEnabled: false, checkDayOfWeek: 0, checkHour: 8, checkMinute: 0, lastCheckAt: null as string | null },
|
||||
{ type: 4, name: '延毕风险', threshold: 5, isEnabled: true, notifyStudent: true, notifyCounselor: true, description: '', autoCheckEnabled: false, checkDayOfWeek: 0, checkHour: 8, checkMinute: 0, lastCheckAt: null as string | null },
|
||||
])
|
||||
|
||||
async function load() {
|
||||
@@ -53,9 +53,13 @@ async function load() {
|
||||
row.notifyStudent = sr.notifyStudent
|
||||
row.notifyCounselor = sr.notifyCounselor
|
||||
row.description = sr.description || ''
|
||||
row.autoCheckEnabled = sr.autoCheckEnabled
|
||||
row.checkDayOfWeek = sr.checkDayOfWeek
|
||||
row.checkHour = sr.checkHour
|
||||
row.checkMinute = sr.checkMinute
|
||||
row.lastCheckAt = sr.lastCheckAt
|
||||
}
|
||||
}
|
||||
await loadSchedule()
|
||||
}
|
||||
if (isSuperAdmin.value || isCounselor.value)
|
||||
records.value = (await http.get('/warnings/records', { params: { academicTermId: termId.value || undefined, type: filterType.value || undefined } })).data
|
||||
@@ -63,19 +67,9 @@ async function load() {
|
||||
} catch (e) { ElMessage.error(apiErrorMessage(e)) } finally { loading.value = false }
|
||||
}
|
||||
|
||||
async function loadSchedule() {
|
||||
if (!termId.value) return
|
||||
try { const { data } = await http.get('/warnings/schedule', { params: { academicTermId: termId.value } }); Object.assign(schedule, data) }
|
||||
catch (_) {}
|
||||
}
|
||||
async function saveSchedule() {
|
||||
try { await http.put('/warnings/schedule', { isEnabled: schedule.isEnabled, dayOfWeek: schedule.dayOfWeek, hour: schedule.hour, minute: schedule.minute }, { params: { academicTermId: termId.value } }); ElMessage.success('已保存') }
|
||||
catch (e) { ElMessage.error(apiErrorMessage(e)) }
|
||||
}
|
||||
|
||||
async function saveRules() {
|
||||
try {
|
||||
const payload = ruleRows.map(r => ({ type: r.type, name: r.name, threshold: r.threshold, isEnabled: r.isEnabled, notifyStudent: r.notifyStudent, notifyCounselor: r.notifyCounselor, description: r.description }))
|
||||
const payload = ruleRows.map(r => ({ type: r.type, name: r.name, threshold: r.threshold, isEnabled: r.isEnabled, notifyStudent: r.notifyStudent, notifyCounselor: r.notifyCounselor, description: r.description, autoCheckEnabled: r.autoCheckEnabled, checkDayOfWeek: r.checkDayOfWeek, checkHour: r.checkHour, checkMinute: r.checkMinute }))
|
||||
await http.put('/warnings/rules', payload, { params: { academicTermId: termId.value } })
|
||||
ElMessage.success('预警规则已保存')
|
||||
} catch (e) { ElMessage.error(apiErrorMessage(e)) }
|
||||
@@ -114,26 +108,6 @@ onMounted(async () => {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Admin: Schedule config -->
|
||||
<section v-if="isSuperAdmin && termId" class="warn-schedule">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center">
|
||||
<h3 style="margin:0">定时检测</h3>
|
||||
<el-button type="primary" size="small" @click="saveSchedule">保存定时</el-button>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:12px;margin-top:10px;flex-wrap:wrap">
|
||||
<el-switch v-model="schedule.isEnabled" active-text="启用自动检测" />
|
||||
<span>每</span>
|
||||
<el-select v-model="schedule.dayOfWeek" size="small" style="width:100px" :disabled="!schedule.isEnabled">
|
||||
<el-option v-for="d in 7" :key="d" :label="['','周一','周二','周三','周四','周五','周六','周日'][d]" :value="d" />
|
||||
</el-select>
|
||||
<el-input-number v-model="schedule.hour" :min="0" :max="23" size="small" style="width:80px" :disabled="!schedule.isEnabled" />
|
||||
<span>时</span>
|
||||
<el-input-number v-model="schedule.minute" :min="0" :max="59" size="small" style="width:80px" :disabled="!schedule.isEnabled" />
|
||||
<span>分</span>
|
||||
<span v-if="schedule.lastRunAt" style="font-size:11px;color:var(--muted);margin-left:auto">上次执行:{{ new Date(schedule.lastRunAt).toLocaleString('zh-CN') }}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Admin: Rule config -->
|
||||
<section v-if="isSuperAdmin && termId" class="warn-rules">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
|
||||
@@ -156,10 +130,28 @@ onMounted(async () => {
|
||||
<el-switch v-model="row.isEnabled" size="small" active-text="启用" style="margin-right:12px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="通知" width="200">
|
||||
<el-table-column label="通知" width="180">
|
||||
<template #default="{row}">
|
||||
<el-checkbox v-model="row.notifyStudent" :disabled="!row.isEnabled">学生</el-checkbox>
|
||||
<el-checkbox v-model="row.notifyCounselor" :disabled="!row.isEnabled" style="margin-left:12px">辅导员</el-checkbox>
|
||||
<el-checkbox v-model="row.notifyCounselor" :disabled="!row.isEnabled" style="margin-left:8px">辅导员</el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="自动检测" min-width="290">
|
||||
<template #default="{row}">
|
||||
<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap">
|
||||
<el-switch v-model="row.autoCheckEnabled" size="small" :disabled="!row.isEnabled" />
|
||||
<template v-if="row.autoCheckEnabled">
|
||||
<span style="font-size:11px">每</span>
|
||||
<el-select v-model="row.checkDayOfWeek" size="small" style="width:75px">
|
||||
<el-option v-for="o in weekOptions" :key="String(o.value)" :label="o.label" :value="o.value" />
|
||||
</el-select>
|
||||
<el-input-number v-model="row.checkHour" :min="0" :max="23" size="small" :controls="false" style="width:56px" />
|
||||
<span style="font-size:11px">时</span>
|
||||
<el-input-number v-model="row.checkMinute" :min="0" :max="59" size="small" :controls="false" style="width:56px" />
|
||||
<span style="font-size:11px">分</span>
|
||||
<span v-if="row.lastCheckAt" style="font-size:10px;color:var(--muted)">上次: {{ new Date(row.lastCheckAt).toLocaleString('zh-CN') }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="说明"><template #default="{row}"><el-input v-model="row.description" size="small" placeholder="可选" maxlength="300" /></template></el-table-column>
|
||||
@@ -198,7 +190,6 @@ onMounted(async () => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.warn-schedule { background:#f0f9eb; border:1px solid #c6e2ff; border-radius:8px; padding:14px 20px; margin-bottom:16px; }
|
||||
.warn-rules { background:#fff; border:1px solid #e4e7ed; border-radius:8px; padding:16px 20px; margin-bottom:20px; }
|
||||
.warn-records { background:#fff; border:1px solid #e4e7ed; border-radius:8px; padding:16px 20px; }
|
||||
.warn-mine { display:grid; gap:10px; }
|
||||
|
||||
Reference in New Issue
Block a user