fix: 系统检查修复 — 布局一致性 + bug 修复

- 公告图片样式统一:圆角 $radius-md,间距 $space-sm
- 首页紧急公告弹窗样式改为 SCSS 变量
- 新增 modal-mask-high mixin 统一高层级弹窗
- admin/config.vue 添加 initialLoaded 守卫避免重复请求
- notifications/index.vue 添加 initialLoaded 守卫
- notification store 所有方法添加 try-catch 错误处理
This commit is contained in:
2026-06-08 11:21:08 +08:00
parent ecfb67872d
commit 24166aeb62
6 changed files with 70 additions and 58 deletions

View File

@@ -60,6 +60,7 @@ import Icon from '@/components/Icon/Icon.vue'
const loading = ref(true) const loading = ref(true)
const saving = ref(false) const saving = ref(false)
const initialLoaded = ref(false)
const form = reactive({ const form = reactive({
app_version: '', app_version: '',
@@ -70,30 +71,26 @@ const form = reactive({
onMounted(async () => { onMounted(async () => {
await waitForReady() await waitForReady()
try { try {
const config = await getConfig() await loadConfig()
form.app_version = config.app_version || '1.0.0'
form.app_slogan = config.app_slogan || ''
form.app_description = config.app_description || ''
} catch (e: any) { } catch (e: any) {
uni.showToast({ title: e.message || '加载失败', icon: 'none' }) uni.showToast({ title: e.message || '加载失败', icon: 'none' })
} finally { } finally {
loading.value = false loading.value = false
initialLoaded.value = true
} }
}) })
// 加载配置数据 // 加载配置数据
async function loadConfig() { async function loadConfig() {
try { const config = await getConfig()
const config = await getConfig() form.app_version = config.app_version || '1.0.0'
form.app_version = config.app_version || '1.0.0' form.app_slogan = config.app_slogan || ''
form.app_slogan = config.app_slogan || '' form.app_description = config.app_description || ''
form.app_description = config.app_description || ''
} catch {}
} }
// 返回页面时静默刷新 // 返回页面时静默刷新
onShow(() => { onShow(() => {
loadConfig() if (initialLoaded.value) loadConfig()
}) })
onPullDownRefresh(async () => { onPullDownRefresh(async () => {

View File

@@ -552,23 +552,14 @@ function goBills() {
/* 强提醒公告弹窗 */ /* 强提醒公告弹窗 */
.modal-mask { .modal-mask {
position: fixed; @include modal-mask-high;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 300;
display: flex;
align-items: center;
justify-content: center;
} }
.urgent-modal { .urgent-modal {
width: 80%; width: 80%;
max-height: 70vh; max-height: 70vh;
background: #FFFFFF; background: $surface;
border-radius: 32rpx; border-radius: $radius-2xl;
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -577,52 +568,52 @@ function goBills() {
.urgent-header { .urgent-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12rpx; gap: $space-sm;
padding: 32rpx 32rpx 16rpx; padding: $space-xl $space-lg $space-md;
} }
.urgent-title { .urgent-title {
font-size: 32rpx; font-size: $font-xl;
font-weight: 600; font-weight: 600;
color: #2D1B1B; color: $text;
flex: 1; flex: 1;
} }
.urgent-body { .urgent-body {
flex: 1; flex: 1;
padding: 0 32rpx 24rpx; padding: 0 $space-lg $space-lg;
max-height: 50vh; max-height: 50vh;
} }
.urgent-image { .urgent-image {
width: 100%; width: 100%;
border-radius: 16rpx; border-radius: $radius-md;
margin-bottom: 16rpx; margin-bottom: $space-sm;
} }
.urgent-content { .urgent-content {
font-size: 28rpx; font-size: $font-lg;
color: #2D1B1B; color: $text;
line-height: 1.6; line-height: 1.6;
} }
.urgent-link { .urgent-link {
margin-top: 16rpx; margin-top: $space-sm;
padding: 12rpx 0; padding: $space-xs 0;
} }
.urgent-link-text { .urgent-link-text {
font-size: 28rpx; font-size: $font-lg;
color: #FF8C69; color: $primary;
text-decoration: underline; text-decoration: underline;
} }
.urgent-footer { .urgent-footer {
padding: 24rpx 32rpx; padding: $space-lg;
border-top: 2rpx solid #F0E0D6; border-top: 2rpx solid $border;
text-align: center; text-align: center;
&:active { background: #FFF8F0; } &:active { background: $surface-warm; }
} }
.urgent-btn-text { .urgent-btn-text {

View File

@@ -104,6 +104,7 @@ const page = ref(1)
const total = ref(0) const total = ref(0)
const loading = ref(false) const loading = ref(false)
const detailItem = ref<Notification | null>(null) const detailItem = ref<Notification | null>(null)
const initialLoaded = ref(false)
let loadSeq = 0 let loadSeq = 0
const noMore = computed(() => list.value.length >= total.value && total.value > 0) const noMore = computed(() => list.value.length >= total.value && total.value > 0)
@@ -111,9 +112,11 @@ const noMore = computed(() => list.value.length >= total.value && total.value >
onMounted(async () => { onMounted(async () => {
await waitForReady() await waitForReady()
await loadNotifications(true) await loadNotifications(true)
initialLoaded.value = true
}) })
onShow(() => { onShow(() => {
if (!initialLoaded.value) return
notifStore.fetchUnreadCount() notifStore.fetchUnreadCount()
// 返回页面时刷新列表(如全部已读后) // 返回页面时刷新列表(如全部已读后)
if (list.value.length > 0) { if (list.value.length > 0) {
@@ -374,8 +377,8 @@ onReachBottom(() => {
.notif-image { .notif-image {
width: 100%; width: 100%;
border-radius: $radius-sm; border-radius: $radius-md;
margin-top: 12rpx; margin-top: $space-sm;
} }
.notif-meta { .notif-meta {

View File

@@ -562,13 +562,7 @@ async function exportData() {
} }
.modal-mask { .modal-mask {
position: fixed; @include modal-mask-high;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
} }
.modal { .modal {

View File

@@ -8,26 +8,42 @@ export const useNotificationStore = defineStore('notification', () => {
const urgentNotifications = ref<Notification[]>([]) const urgentNotifications = ref<Notification[]>([])
async function fetchUnreadCount() { async function fetchUnreadCount() {
const data = await api.getUnreadCount() try {
unreadCount.value = data.count const data = await api.getUnreadCount()
unreadCount.value = data.count
} catch {
// 静默失败,不影响页面
}
} }
/** 获取未读的强提醒公告(首页弹窗用) */ /** 获取未读的强提醒公告(首页弹窗用) */
async function fetchUrgentNotifications() { async function fetchUrgentNotifications() {
urgentNotifications.value = await api.getPinnedNotifications() try {
urgentNotifications.value = await api.getPinnedNotifications()
} catch {
// 静默失败
}
} }
async function markRead(id: number) { async function markRead(id: number) {
await api.markRead(id) try {
if (unreadCount.value > 0) unreadCount.value-- await api.markRead(id)
// 从强提醒列表中移除 if (unreadCount.value > 0) unreadCount.value--
urgentNotifications.value = urgentNotifications.value.filter(n => n.id !== id) // 从强提醒列表中移除
urgentNotifications.value = urgentNotifications.value.filter(n => n.id !== id)
} catch {
// 静默失败
}
} }
async function markAllRead() { async function markAllRead() {
await api.markAllRead() try {
unreadCount.value = 0 await api.markAllRead()
urgentNotifications.value = [] unreadCount.value = 0
urgentNotifications.value = []
} catch {
// 静默失败
}
} }
return { unreadCount, urgentNotifications, fetchUnreadCount, fetchUrgentNotifications, markRead, markAllRead } return { unreadCount, urgentNotifications, fetchUnreadCount, fetchUrgentNotifications, markRead, markAllRead }

View File

@@ -109,6 +109,17 @@
align-items: flex-end; align-items: flex-end;
} }
// 高层级弹窗(紧急公告、身份选择等需要覆盖其他弹窗的场景)
@mixin modal-mask-high {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 300;
display: flex;
align-items: center;
justify-content: center;
}
@mixin bottom-modal { @mixin bottom-modal {
width: 100%; width: 100%;
max-height: 80vh; max-height: 80vh;