system.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. <template>
  2. <view class="system-page">
  3. <view class="header">
  4. <text class="header-title">📢 系统消息</text>
  5. <button class="read-all" @click="markAllRead" size="mini">全部已读</button>
  6. </view>
  7. <scroll-view class="list" scroll-y @scrolltolower="loadMore">
  8. <view v-for="item in list" :key="item.id" class="item" @click="openDetail(item)">
  9. <view class="left">
  10. <text class="icon">📢</text>
  11. <view v-if="item.isRead===0" class="dot"></view>
  12. </view>
  13. <view class="content">
  14. <view class="title">{{ item.title || '系统通知' }}</view>
  15. <view class="desc">{{ item.content }}</view>
  16. </view>
  17. <view class="time">{{ formatTime(item.createdAt || item.created_at) }}</view>
  18. </view>
  19. <view class="load-more" v-if="hasMore">{{ loading ? '加载中...' : '上拉加载更多' }}</view>
  20. <view class="load-more" v-else>没有更多了</view>
  21. </scroll-view>
  22. </view>
  23. </template>
  24. <script>
  25. import api from '@/utils/api.js'
  26. export default {
  27. data(){
  28. return { userId: null, list: [], pageNum:1, pageSize:20, hasMore:true, loading:false }
  29. },
  30. onLoad(){
  31. this.userId = uni.getStorageSync('userId')
  32. this.loadList()
  33. },
  34. methods:{
  35. async loadList(){
  36. if(this.loading || !this.hasMore) return
  37. this.loading = true
  38. try{
  39. const res = await api.message.getSystemList(this.userId, this.pageNum, this.pageSize)
  40. const data = res.list || []
  41. this.list = this.list.concat(data)
  42. const total = (typeof res.total === 'number') ? res.total : this.list.length
  43. this.hasMore = this.list.length < total
  44. this.pageNum++
  45. }catch(e){ console.log('加载失败',e) }
  46. finally{ this.loading=false }
  47. },
  48. loadMore(){ this.loadList() },
  49. async markAllRead(){
  50. try{ await api.message.markAllSystemRead(this.userId); uni.showToast({title:'已全部已读',icon:'success'}) }
  51. catch(e){ uni.showToast({title:'操作失败',icon:'none'}) }
  52. },
  53. async openDetail(item){
  54. try{
  55. const data = await api.message.getSystemDetail(item.id)
  56. if(item.isRead===0){ try{ await api.message.markSystemRead(item.id); item.isRead=1 } catch(e){} }
  57. uni.showModal({
  58. title: data.title || '系统通知',
  59. content: (data.content || '').replace(/\n/g,'\n'),
  60. showCancel:false
  61. })
  62. }catch(e){
  63. uni.showToast({ title:'获取详情失败', icon:'none' })
  64. }
  65. },
  66. formatTime(t){ if(!t) return ''; const d=new Date(t); return d.toLocaleString('zh-CN',{hour:'2-digit',minute:'2-digit',month:'2-digit',day:'2-digit'}) }
  67. }
  68. }
  69. </script>
  70. <style scoped>
  71. .system-page{min-height:100vh;background:#F7F7F7}
  72. .header{display:flex;justify-content:space-between;align-items:center;padding:20rpx 24rpx;background:#fff;border-bottom:1rpx solid #eee}
  73. .header-title{font-size:34rpx;color:#E91E63;font-weight:600}
  74. .read-all{font-size:24rpx}
  75. .list{height:calc(100vh - 100rpx)}
  76. .item{display:flex;align-items:flex-start;padding:24rpx;background:#fff;border-bottom:1rpx solid #F2F2F2}
  77. .left{position:relative;margin-right:20rpx}
  78. .icon{font-size:44rpx}
  79. .dot{position:absolute;right:-6rpx;top:-6rpx;width:16rpx;height:16rpx;background:#FA5151;border-radius:50%}
  80. .content{flex:1}
  81. .title{font-size:30rpx;color:#333;margin-bottom:6rpx}
  82. .desc{font-size:26rpx;color:#777}
  83. .time{font-size:24rpx;color:#999;margin-left:12rpx}
  84. .load-more{padding:24rpx;color:#999;text-align:center}
  85. </style>