feat: 全面公告系统 + 全系统 Bug 修复
全面公告系统: - notifications 表新增置顶/强提醒/定时发布/过期/配图/链接字段 - 后端增强:7 个接口(列表/置顶/未读数/已读/发布/编辑/删除) - 首页强提醒公告弹窗 - 通知中心:置顶标记、配图展示、链接跳转、管理员操作 - 管理员发布表单:标题+内容+链接+配图+置顶+强提醒+定时+过期 Bug 修复: - 401 重试队列竞态导致请求永久挂起 - 多分类筛选条件被静默丢弃 - 网络错误导致群组选择丢失 - Numpad 输入 '.' 显示异常 - transaction store 错误时清空数据 - 首页/预算页 Promise.all 部分失败阻塞关键数据 - CSS prefers-reduced-motion 类名不匹配 - 通知 read-all 遗漏群组通知 - auth.ts 错误日志缺少 stack trace - stats 页面 v-for key 使用 index - add 页面重复调用 fetchCategories
This commit is contained in:
@@ -112,6 +112,26 @@
|
||||
<Icon name="edit" :size="48" color="#BFB3B3" />
|
||||
<text class="state-text">还没有记录,快去记一笔吧</text>
|
||||
</view>
|
||||
|
||||
<!-- 强提醒公告弹窗 -->
|
||||
<view class="modal-mask" v-if="urgentNotice" @tap="dismissUrgent">
|
||||
<view class="urgent-modal" @tap.stop>
|
||||
<view class="urgent-header">
|
||||
<Icon name="bell" :size="32" color="#FF8C69" />
|
||||
<text class="urgent-title">{{ urgentNotice.title }}</text>
|
||||
</view>
|
||||
<scroll-view class="urgent-body" scroll-y>
|
||||
<image v-if="urgentNotice.image_url" class="urgent-image" :src="urgentNotice.image_url" mode="widthFix" />
|
||||
<text class="urgent-content">{{ urgentNotice.content }}</text>
|
||||
<view v-if="urgentNotice.link_url" class="urgent-link" @tap="openLink(urgentNotice.link_url)">
|
||||
<text class="urgent-link-text">查看详情</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="urgent-footer" @tap="dismissUrgent">
|
||||
<text class="urgent-btn-text">我知道了</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -160,6 +180,27 @@ const greeting = computed(() => {
|
||||
return '晚上好'
|
||||
})
|
||||
|
||||
// 强提醒公告:取第一条未读的
|
||||
const urgentNotice = computed(() => notifStore.urgentNotifications[0] || null)
|
||||
|
||||
function dismissUrgent() {
|
||||
if (urgentNotice.value) {
|
||||
notifStore.markRead(urgentNotice.value.id)
|
||||
}
|
||||
}
|
||||
|
||||
function openLink(url: string) {
|
||||
// #ifdef H5
|
||||
window.open(url, '_blank')
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.setClipboardData({
|
||||
data: url,
|
||||
success: () => uni.showToast({ title: '链接已复制', icon: 'success' })
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
async function loadData(silent = false) {
|
||||
await waitForReady()
|
||||
try {
|
||||
@@ -167,15 +208,21 @@ async function loadData(silent = false) {
|
||||
loadError.value = false
|
||||
const now = new Date()
|
||||
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
||||
// 关键数据:任一失败则显示错误
|
||||
await Promise.all([
|
||||
statsStore.fetchOverview(),
|
||||
budgetStore.fetchBudget(),
|
||||
txStore.fetchTransactions({ page: 1 }),
|
||||
fetchTodayData(today),
|
||||
userStore.fetchUserInfo(),
|
||||
notifStore.fetchUnreadCount()
|
||||
fetchTodayData(today)
|
||||
])
|
||||
recentTx.value = txStore.transactions.slice(0, 5)
|
||||
|
||||
// 非关键数据:失败不影响页面显示
|
||||
Promise.allSettled([
|
||||
userStore.fetchUserInfo(),
|
||||
notifStore.fetchUnreadCount(),
|
||||
notifStore.fetchUrgentNotifications()
|
||||
])
|
||||
} catch (e) {
|
||||
console.error('Load error:', e)
|
||||
if (!silent) loadError.value = true
|
||||
@@ -516,4 +563,85 @@ function goBills() {
|
||||
font-size: 28rpx;
|
||||
color: #BFB3B3;
|
||||
}
|
||||
|
||||
/* 强提醒公告弹窗 */
|
||||
.modal-mask {
|
||||
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;
|
||||
}
|
||||
|
||||
.urgent-modal {
|
||||
width: 80%;
|
||||
max-height: 70vh;
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.urgent-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
padding: 32rpx 32rpx 16rpx;
|
||||
}
|
||||
|
||||
.urgent-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #2D1B1B;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.urgent-body {
|
||||
flex: 1;
|
||||
padding: 0 32rpx 24rpx;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
.urgent-image {
|
||||
width: 100%;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.urgent-content {
|
||||
font-size: 28rpx;
|
||||
color: #2D1B1B;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.urgent-link {
|
||||
margin-top: 16rpx;
|
||||
padding: 12rpx 0;
|
||||
}
|
||||
|
||||
.urgent-link-text {
|
||||
font-size: 28rpx;
|
||||
color: #FF8C69;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.urgent-footer {
|
||||
padding: 24rpx 32rpx;
|
||||
border-top: 2rpx solid #F0E0D6;
|
||||
text-align: center;
|
||||
|
||||
&:active { background: #FFF8F0; }
|
||||
}
|
||||
|
||||
.urgent-btn-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #FF8C69;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user