统一通知中心
This commit is contained in:
1 parent
13b61f191a
commit
2c6103d3ae
8 files changed
+552
-144
No files matched your search
@@ -1,9 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { Operation } from '@element-plus/icons-vue'
|
||||
import { Bell, Operation } from '@element-plus/icons-vue'
|
||||
import http from '../api/http'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
|
||||
const unreadNotifCount = ref(0)
|
||||
|
||||
async function pollUnread() {
|
||||
try {
|
||||
const { data } = await http.get('/notifications/unread-count')
|
||||
unreadNotifCount.value = data.count
|
||||
} catch (_) { /* ignore */ }
|
||||
}
|
||||
|
||||
interface NavigationItem {
|
||||
path: string
|
||||
label: string
|
||||
@@ -197,7 +207,11 @@ function signOut() {
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
onMounted(() => auth.refresh().catch(() => undefined))
|
||||
onMounted(() => {
|
||||
auth.refresh().catch(() => undefined)
|
||||
pollUnread()
|
||||
setInterval(pollUnread, 60000)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -229,6 +243,9 @@ onMounted(() => auth.refresh().catch(() => undefined))
|
||||
</div>
|
||||
|
||||
<div class="user-block">
|
||||
<el-badge :value="unreadNotifCount" :hidden="!unreadNotifCount" :max="99">
|
||||
<el-button :icon="Bell" circle text @click="router.push('/notifications')" title="消息通知" />
|
||||
</el-badge>
|
||||
<div class="avatar">{{ auth.user?.displayName?.slice(0, 1) ?? '管' }}</div>
|
||||
<div class="user-copy">
|
||||
<b>{{ auth.user?.displayName ?? '系统管理员' }}</b>
|
||||
|
||||
@@ -197,6 +197,11 @@ const router = createRouter({
|
||||
roles: ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Teacher'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'notifications',
|
||||
name: 'notifications',
|
||||
component: () => import('../views/NotificationCenterView.vue'),
|
||||
},
|
||||
{
|
||||
path: 'student-status-changes',
|
||||
name: 'student-status-changes',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { Bell, Check, Close, Plus, Refresh } from '@element-plus/icons-vue'
|
||||
import { Bell, Check, Plus, Refresh } from '@element-plus/icons-vue'
|
||||
import http, { apiErrorMessage } from '../api/http'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
|
||||
@@ -41,7 +41,7 @@ const form = reactive({
|
||||
|
||||
const typeLabels: Record<string, string> = { Reschedule: '调课', Cancel: '停课', Makeup: '补课', Substitute: '代课' }
|
||||
const statusLabels: Record<string, string> = { Draft: '草稿', Submitted: '待审核', Approved: '已通过', Rejected: '已退回' }
|
||||
const statusColors: Record<string, string> = { Draft: 'info', Submitted: 'warning', Approved: 'success', Rejected: 'danger' }
|
||||
const statusColors: Record<string, 'info' | 'warning' | 'success' | 'danger'> = { Draft: 'info', Submitted: 'warning', Approved: 'success', Rejected: 'danger' }
|
||||
|
||||
function periodOptions() {
|
||||
return Array.from({ length: 12 }, (_, i) => ({ value: i + 1, label: `第 ${i + 1} 节` }))
|
||||
@@ -67,7 +67,7 @@ async function load() {
|
||||
|
||||
async function loadNotifications() {
|
||||
try {
|
||||
const { data } = await http.get('/course-adjustments/notifications')
|
||||
const { data } = await http.get('/notifications', { params: { pageSize: 50 } })
|
||||
notifications.value = data.items
|
||||
unreadCount.value = data.unreadCount
|
||||
} catch (_) { /* notifications are optional */ }
|
||||
@@ -148,7 +148,7 @@ async function doReject() {
|
||||
async function markRead(n: any) {
|
||||
if (n.isRead) return
|
||||
try {
|
||||
await http.post(`/course-adjustments/notifications/${n.id}/read`)
|
||||
await http.post(`/notifications/${n.id}/read`)
|
||||
n.isRead = true
|
||||
unreadCount.value = Math.max(0, unreadCount.value - 1)
|
||||
} catch (_) {}
|
||||
@@ -156,7 +156,7 @@ async function markRead(n: any) {
|
||||
|
||||
async function markAllRead() {
|
||||
try {
|
||||
await http.post('/course-adjustments/notifications/read-all')
|
||||
await http.post('/notifications/read-all')
|
||||
notifications.value.forEach(n => n.isRead = true)
|
||||
unreadCount.value = 0
|
||||
} catch (_) {}
|
||||
@@ -340,7 +340,6 @@ function showCancel(type: string) { return type === 'Cancel' }
|
||||
</div>
|
||||
<el-form-item label="教室(可选)">
|
||||
<el-select v-model="form.classroomId" clearable filterable placeholder="留空不调整教室">
|
||||
<el-option v-for="r in []" :key="r" /> <!-- classrooms loaded separately if needed -->
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
@@ -348,7 +347,6 @@ function showCancel(type: string) { return type === 'Cancel' }
|
||||
<template v-if="showSub(form.type)">
|
||||
<el-form-item label="代课教师" required>
|
||||
<el-select v-model="form.substituteTeacherId" filterable placeholder="选择代课教师">
|
||||
<el-option v-for="t in []" :key="t" /> <!-- teachers loaded separately if needed -->
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
Loaded 3 of 8 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user