zhangwencui
3 天以前 8d4a66c31aa0f6418403ade8a780f27c9fa9d921
消息列表
已添加1个文件
已修改3个文件
513 ■■■■■ 文件已修改
src/api/login.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages.json 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/login.vue 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/message.vue 423 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/login.js
@@ -57,4 +57,23 @@
    method: 'post',
    data: data
  })
}
// æŸ¥è¯¢å…¬å‘Šåˆ—表
export function listNotice(query) {
  return request({
    url: '/system/notice/list',
    method: 'get',
    params: query
  })
}
// èŽ·å–æœªè¯»æ¶ˆæ¯æ•°é‡
export function getNoticeCount(consigneeId) {
  return request({
    url: '/system/notice/getCount',
    method: 'get',
    params: { consigneeId }
  })
}
src/pages.json
@@ -884,6 +884,12 @@
        "navigationBarTitleText": "薪资台账详情",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/message",
      "style": {
        "navigationBarTitleText": "消息中心"
      }
    }
  ],
  "subPackages": [
@@ -1096,6 +1102,12 @@
        "text": "首页"
      },
      {
        "pagePath": "pages/message",
        "iconPath": "static/images/tabbar/work.png",
        "selectedIconPath": "static/images/tabbar/work_.png",
        "text": "消息"
      },
      {
        "pagePath": "pages/mine",
        "iconPath": "static/images/tabbar/mine.png",
        "selectedIconPath": "static/images/tabbar/mine_.png",
src/pages/login.vue
@@ -48,7 +48,11 @@
      icon: "none",
    });
  };
  import { userLoginFacotryList, updateClientId } from "@/api/login";
  import {
    userLoginFacotryList,
    updateClientId,
    getNoticeCount,
  } from "@/api/login";
  import { ref, onMounted } from "vue";
  import useUserStore from "@/store/modules/user";
  import { getWxCode } from "@/utils/geek";
@@ -160,20 +164,57 @@
  function loginSuccess(result) {
    // è®¾ç½®ç”¨æˆ·ä¿¡æ¯
    userStore.getInfo().then(res => {
      const userId = res.user.userId;
      // èŽ·å–è·¯ç”±æƒé™
      userStore.getRouters().then(() => {
        console.log("路由权限获取成功");
      }).catch(error => {
        console.error("获取路由权限失败:", error);
      });
      userStore
        .getRouters()
        .then(() => {
          console.log("路由权限获取成功");
        })
        .catch(error => {
          console.error("获取路由权限失败:", error);
        });
      // ç™»å½•成功后,将客户端推送标识发送到服务器
      sendClientIdToServer();
      uni.switchTab({
        url: "/pages/index",
      });
      // å¯åŠ¨å®šæ—¶èŽ·å–æœªè¯»æ¶ˆæ¯æ•°é‡çš„å®šæ—¶å™¨
      startNoticeCountTimer(userId);
      uni.switchTab({ url: "/pages/index" });
    });
  }
  // å¯åŠ¨å®šæ—¶èŽ·å–æœªè¯»æ¶ˆæ¯æ•°é‡çš„å®šæ—¶å™¨
  function startNoticeCountTimer(userId) {
    // ç«‹å³èŽ·å–ä¸€æ¬¡æœªè¯»æ¶ˆæ¯æ•°é‡
    updateNoticeCount(userId);
    // è®¾ç½®å®šæ—¶å™¨ï¼Œæ¯30秒获取一次
    setInterval(() => {
      updateNoticeCount(userId);
    }, 30000);
  }
  // æ›´æ–°æœªè¯»æ¶ˆæ¯æ•°é‡
  function updateNoticeCount(userId) {
    getNoticeCount(userId)
      .then(res => {
        const count = res.data || 0;
        console.log("未读消息数量:", count);
        // æ›´æ–°tabbar的角标
        if (count > 0) {
          uni.setTabBarBadge({
            index: 1, // æ¶ˆæ¯æ ‡ç­¾é¡µçš„索引
            text: count.toString(),
          });
        } else {
          uni.removeTabBarBadge({
            index: 1,
          });
        }
      })
      .catch(error => {
        console.error("获取未读消息数量失败:", error);
      });
  }
  // å°†å®¢æˆ·ç«¯æŽ¨é€æ ‡è¯†å‘送到服务器
  function sendClientIdToServer() {
    // èŽ·å–æœ¬åœ°å­˜å‚¨çš„å®¢æˆ·ç«¯æ ‡è¯†
src/pages/message.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,423 @@
<template>
  <view class="message-page">
    <!-- é¡µé¢å¤´éƒ¨ -->
    <!-- <PageHeader title="消息中心"
                :showBack="false" /> -->
    <!-- ç­›é€‰æ ‡ç­¾ -->
    <view class="tabs-container">
      <up-tabs v-model="activeTab"
               @change="handleTabChange"
               :list="tabList"
               :current="activeTab"
               itemStyle="width: 50%;height: 80rpx;"></up-tabs>
    </view>
    <!-- æ¶ˆæ¯åˆ—表 -->
    <scroll-view class="message-list"
                 scroll-y="true"
                 refresher-enabled="true"
                 :refresher-triggered="triggered"
                 :refresher-threshold="100"
                 refresher-background="#f5f7fa"
                 @refresherrefresh="onRefresh"
                 @scrolltolower="loadMore">
      <!-- åŠ è½½çŠ¶æ€ -->
      <view v-if="loading"
            class="loading-state">
        <text class="loading-text">加载中...</text>
      </view>
      <!-- æ¶ˆæ¯åˆ—表 -->
      <view v-else
            v-for="(item) in messageList"
            :key="item.id"
            class="message-item"
            :class="{ 'unread': !item.read }">
        <view class="message-content">
          <view class="message-header">
            <text class="message-title">{{ item.noticeTitle }}</text>
            <text class="message-time">{{ formatTime(item.createTime) }}</text>
          </view>
          <text class="message-desc">{{ item.noticeContent }}</text>
          <view class="message-footer">
            <text class="message-view"
                  @click="goToDetail(item)">
              åŽ»æŸ¥çœ‹ >
            </text>
          </view>
        </view>
      </view>
      <!-- ç©ºçŠ¶æ€ -->
      <view v-if="!loading && messageList.length === 0"
            class="empty-state">
        <text class="empty-text">暂无消息</text>
      </view>
      <!-- åŠ è½½æ›´å¤šçŠ¶æ€ -->
      <view v-if="loadingMore"
            class="loading-more-state">
        <text class="loading-more-text">加载更多...</text>
      </view>
    </scroll-view>
  </view>
</template>
<script setup>
  import { ref, reactive, onMounted } from "vue";
  import { listNotice } from "@/api/login.js";
  import useUserStore from "@/store/modules/user";
  // æ ‡ç­¾é¡µæ•°æ®
  const tabList = [
    { name: "未读", id: 0 },
    { name: "已读", id: 1 },
  ];
  // å½“前激活的标签
  const activeTab = ref(0);
  // æ¶ˆæ¯åˆ—表数据
  const messageList = ref([]);
  const loading = ref(false);
  const loadingMore = ref(false);
  const total = ref(0);
  const triggered = ref(false);
  // åˆ†é¡µå‚æ•°
  const page = reactive({
    current: 1,
    size: 10,
  });
  // æ ¼å¼åŒ–æ—¶é—´
  const formatTime = time => {
    if (!time) return "";
    const date = new Date(time);
    const Y = date.getFullYear();
    const M = String(date.getMonth() + 1).padStart(2, "0");
    const D = String(date.getDate()).padStart(2, "0");
    const h = String(date.getHours()).padStart(2, "0");
    const m = String(date.getMinutes()).padStart(2, "0");
    return `${Y}-${M}-${D} ${h}:${m}`;
  };
  // è·³è½¬åˆ°è¯¦æƒ…页
  const goToDetail = item => {
    if (item.jumpPath.indexOf("/") === 0) {
      uni.navigateTo({
        url: item.jumpPath,
      });
    } else {
      uni.navigateTo({
        url: "/" + item.jumpPath,
      });
    }
  };
  const userStore = useUserStore();
  const userId = ref("");
  const getUserId = () => {
    return userStore.getInfo().then(res => {
      console.log(res.user.userId, "res@@@@@@@@@@@2");
      userId.value = res.user.userId;
    });
  };
  // å¤„理标签页切换
  const handleTabChange = val => {
    console.log(val);
    activeTab.value = val.id;
    page.current = 1;
    loadMessages(false);
  };
  // åŠ è½½æ¶ˆæ¯åˆ—è¡¨
  const loadMessages = (append = false) => {
    if (append) {
      loadingMore.value = true;
    } else {
      loading.value = true;
    }
    // æž„建查询参数
    const params = {
      consigneeId: userId.value,
      current: page.current,
      size: page.size,
      status: activeTab.value,
    };
    console.log(params, "===========");
    return listNotice(params)
      .then(res => {
        const records = res?.data?.records || [];
        total.value = res?.data?.total || 0;
        if (append) {
          messageList.value = [...messageList.value, ...records];
        } else {
          messageList.value = records;
        }
      })
      .catch(error => {
        console.error("获取消息失败:", error);
        uni.showToast({ title: "获取消息失败,请重试", icon: "none" });
      })
      .finally(() => {
        loading.value = false;
        loadingMore.value = false;
      });
  };
  // åŠ è½½æ›´å¤š
  const loadMore = () => {
    console.log("===========");
    if (loading.value || loadingMore.value) return;
    if (messageList.value.length >= total.value) return;
    page.current++;
    loadMessages(true);
  };
  // ä¸‹æ‹‰åˆ·æ–°
  const onRefresh = () => {
    triggered.value = true;
    // é‡ç½®é¡µç 
    page.current = 1;
    // é‡æ–°åŠ è½½æ¶ˆæ¯
    loadMessages(false).finally(() => {
      // å…³é—­åˆ·æ–°çŠ¶æ€
      setTimeout(() => {
        triggered.value = false;
      }, 500);
    });
  };
  // é¡µé¢åŠ è½½æ—¶èŽ·å–æ¶ˆæ¯åˆ—è¡¨
  onMounted(() => {
    getUserId().then(() => {
      loadMessages();
    });
  });
</script>
<style scoped lang="scss">
  // å…¨å±€å˜é‡
  $primary-color: #2c7be5;
  $primary-light: #4a90e2;
  $success-color: #4cd964;
  $warning-color: #ff9500;
  $danger-color: #ff3b30;
  $text-primary: #333333;
  $text-secondary: #666666;
  $text-tertiary: #999999;
  $bg-color: #f5f7fa;
  $card-bg: #ffffff;
  $border-color: #e8e8e8;
  $shadow-sm: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
  .message-page {
    min-height: 100vh;
    background-color: $bg-color;
    padding-bottom: 30rpx;
  }
  /* æ ‡ç­¾é¡µå®¹å™¨ */
  .tabs-container {
    background-color: #ffffff;
    margin-bottom: 20rpx;
    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
  }
  /* æ¶ˆæ¯åˆ—表 */
  .message-list {
    margin: 0 20rpx 20rpx;
    min-height: 600rpx;
    height: calc(100vh - 200rpx);
  }
  /* åŠ è½½çŠ¶æ€ */
  .loading-state {
    background-color: $card-bg;
    border-radius: 16rpx;
    box-shadow: $shadow-sm;
    text-align: center;
    padding: 120rpx 0;
    margin-bottom: 20rpx;
  }
  .loading-text {
    font-size: 14px;
    color: $text-tertiary;
    margin-top: 24rpx;
    font-weight: 500;
  }
  /* æ¶ˆæ¯é¡¹ */
  .message-item {
    display: flex;
    align-items: flex-start;
    background-color: $card-bg;
    border-radius: 16rpx;
    box-shadow: $shadow-sm;
    padding: 24rpx;
    margin-bottom: 20rpx;
    margin-right: 40rpx;
    transition: all 0.3s ease;
  }
  .message-item:hover {
    box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.12);
    transform: translateY(-2rpx);
  }
  .message-item.unread {
    border-left: 4rpx solid $primary-color;
  }
  /* æ¶ˆæ¯å›¾æ ‡ */
  .message-icon {
    margin-right: 20rpx;
    margin-top: 4rpx;
  }
  /* æ¶ˆæ¯å†…容 */
  .message-content {
    flex: 1;
  }
  /* æ¶ˆæ¯å¤´éƒ¨ */
  .message-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 12rpx;
  }
  .message-title {
    font-size: 16px;
    font-weight: 600;
    color: $text-primary;
    flex: 1;
    margin-right: 20rpx;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .message-time {
    font-size: 12px;
    color: $text-tertiary;
  }
  /* æ¶ˆæ¯æè¿° */
  .message-desc {
    font-size: 14px;
    color: $text-secondary;
    line-height: 1.4;
    margin-bottom: 16rpx;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* æ¶ˆæ¯åº•部 */
  .message-footer {
    display: flex;
    justify-content: flex-end;
    align-items: center;
  }
  .message-creator {
    font-size: 12px;
    color: $text-tertiary;
  }
  /* ç©ºçŠ¶æ€ */
  .empty-state {
    background-color: $card-bg;
    border-radius: 16rpx;
    box-shadow: $shadow-sm;
    text-align: center;
    padding: 160rpx 0;
    margin: 40rpx 0;
  }
  .empty-text {
    font-size: 14px;
    color: $text-tertiary;
    margin-top: 24rpx;
    font-weight: 500;
  }
  /* åŠ è½½æ›´å¤šçŠ¶æ€ */
  .loading-more-state {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 30rpx 0;
    margin-top: 10rpx;
  }
  .loading-more-text {
    font-size: 14px;
    color: $text-tertiary;
    margin-left: 10rpx;
  }
  /* åŠ è½½æ›´å¤š */
  .load-more {
    text-align: center;
    padding: 30rpx 0;
    font-size: 14px;
    color: $primary-color;
    font-weight: 500;
    margin-top: 20rpx;
  }
  .load-more-text {
    display: inline-block;
    padding: 10rpx 30rpx;
    background-color: rgba($primary-color, 0.1);
    border-radius: 20rpx;
    transition: all 0.3s ease;
  }
  .load-more-text:hover {
    background-color: rgba($primary-color, 0.2);
    transform: translateY(-2rpx);
  }
  /* åŠ¨ç”»æ•ˆæžœ */
  @keyframes fadeInUp {
    from {
      opacity: 0;
      transform: translateY(20rpx);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
  .message-item {
    animation: fadeInUp 0.3s ease-out;
  }
  .message-item:nth-child(2) {
    animation-delay: 0.1s;
  }
  .message-item:nth-child(3) {
    animation-delay: 0.2s;
  }
  .message-item:nth-child(4) {
    animation-delay: 0.3s;
  }
  .message-item:nth-child(5) {
    animation-delay: 0.4s;
  }
  .message-view {
    float: right;
    color: $primary-color;
  }
</style>