Przeglądaj źródła

红包余额不足

wangxy 1 tydzień temu
rodzic
commit
9244fa99fd

+ 29 - 0
src/api/crm/orderAuditMsg.js

@@ -0,0 +1,29 @@
+import request from '@/utils/request'
+
+export function getLatestUnreadMsg() {
+  return request({
+    url: '/crm/orderAuditMsg/getLatestUnread',
+    method: 'get'
+  })
+}
+
+export function getUnreadCount() {
+  return request({
+    url: '/crm/orderAuditMsg/getUnreadCount',
+    method: 'get'
+  })
+}
+
+export function markAsRead(msgId) {
+  return request({
+    url: '/crm/orderAuditMsg/markAsRead/' + msgId,
+    method: 'post'
+  })
+}
+
+export function markAllAsRead() {
+  return request({
+    url: '/crm/orderAuditMsg/markAllAsRead',
+    method: 'post'
+  })
+}

+ 29 - 0
src/api/crm/redPacketBalanceMsg.js

@@ -0,0 +1,29 @@
+import request from '@/utils/request'
+
+export function getLatestUnreadMsg() {
+  return request({
+    url: '/crm/redPacketBalanceMsg/getLatestUnread',
+    method: 'get'
+  })
+}
+
+export function getUnreadCount() {
+  return request({
+    url: '/crm/redPacketBalanceMsg/getUnreadCount',
+    method: 'get'
+  })
+}
+
+export function markAsRead(msgId) {
+  return request({
+    url: '/crm/redPacketBalanceMsg/markAsRead/' + msgId,
+    method: 'post'
+  })
+}
+
+export function markAllAsRead() {
+  return request({
+    url: '/crm/redPacketBalanceMsg/markAllAsRead',
+    method: 'post'
+  })
+}

+ 272 - 0
src/components/OrderAuditMsgDialog/index.vue

@@ -0,0 +1,272 @@
+<template>
+  <el-dialog
+    title="订单审批提醒"
+    :visible.sync="visible"
+    width="480px"
+    :close-on-click-modal="false"
+    :show-close="true"
+    custom-class="order-audit-msg-dialog"
+    @close="handleClose"
+    center
+  >
+    <div class="msg-content">
+      <div class="msg-icon audit">
+        <i class="el-icon-document-checked"></i>
+      </div>
+      <div class="msg-text">
+        <h3>{{ msgData.title }}</h3>
+        <p>{{ msgData.content }}</p>
+        <div class="msg-stats">
+          <div class="stat-item">
+            <span class="stat-label">未处理消息</span>
+            <span class="stat-value">{{ unreadCount }}条</span>
+          </div>
+          <div class="stat-item">
+            <span class="stat-label">未处理率</span>
+            <span class="stat-value percent">{{ unreadPercent }}%</span>
+          </div>
+        </div>
+        <div class="msg-time" v-if="msgData.createTime">
+          <i class="el-icon-time"></i>
+          {{ msgData.createTime }}
+        </div>
+      </div>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="handleKnow" size="medium">
+        <i class="el-icon-check"></i> 知道了
+      </el-button>
+      <el-button type="primary" @click="handleGoProcess" size="medium">
+        <i class="el-icon-right"></i> 去处理
+      </el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import { getLatestUnreadMsg, markAsRead } from '@/api/crm/orderAuditMsg'
+
+export default {
+  name: 'OrderAuditMsgDialog',
+  data() {
+    return {
+      visible: false,
+      msgData: {
+        msgId: null,
+        title: '',
+        content: '',
+        actionUrl: '',
+        createTime: ''
+      },
+      unreadCount: 0,
+      totalCount: 0
+    }
+  },
+  computed: {
+    unreadPercent() {
+      if (this.totalCount === 0) return 0
+      return Math.round((this.unreadCount / this.totalCount) * 100)
+    }
+  },
+  methods: {
+    checkNewMsg() {
+      getLatestUnreadMsg().then(res => {
+        if (res.code === 200 && res.data) {
+          if (res.data.msg) {
+            this.msgData = res.data.msg
+            this.unreadCount = res.data.unreadCount || 0
+            this.totalCount = res.data.totalCount || 0
+            this.visible = true
+          }
+        }
+      }).catch(err => {
+        console.error('获取订单审批提醒消息失败:', err)
+      })
+    },
+    handleKnow() {
+      this.visible = false
+    },
+    handleGoProcess() {
+      if (this.msgData.msgId) {
+        markAsRead(this.msgData.msgId).then(() => {
+          this.visible = false
+          this.$emit('msg-read')
+          if (this.msgData.actionUrl) {
+            this.navigateToUrl(this.msgData.actionUrl)
+          }
+          setTimeout(() => {
+            this.checkNewMsg()
+          }, 500)
+        })
+      } else {
+        this.visible = false
+        if (this.msgData.actionUrl) {
+          this.navigateToUrl(this.msgData.actionUrl)
+        }
+      }
+    },
+    navigateToUrl(url) {
+      if (url.endsWith('/index')) {
+        url = url.slice(0, -6)
+      }
+      if (!url.startsWith('/')) {
+        url = '/' + url
+      }
+      this.$router.push(url).catch(err => {
+        console.error('路由跳转失败:', err)
+      })
+    },
+    handleClose() {
+      this.visible = false
+    },
+    show() {
+      this.checkNewMsg()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.order-audit-msg-dialog {
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.msg-content {
+  display: flex;
+  align-items: flex-start;
+  padding: 24px 16px;
+}
+
+.msg-icon {
+  flex-shrink: 0;
+  width: 56px;
+  height: 56px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 20px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.msg-icon.audit {
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+
+.msg-icon i {
+  font-size: 28px;
+  color: #fff;
+  animation: bell-ring 1s ease-in-out infinite;
+}
+
+@keyframes bell-ring {
+  0%, 100% {
+    transform: rotate(0);
+  }
+  10%, 30% {
+    transform: rotate(10deg);
+  }
+  20%, 40% {
+    transform: rotate(-10deg);
+  }
+  50% {
+    transform: rotate(0);
+  }
+}
+
+.msg-text {
+  flex: 1;
+}
+
+.msg-text h3 {
+  margin: 0 0 12px 0;
+  font-size: 18px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.msg-text p {
+  margin: 0 0 12px 0;
+  font-size: 14px;
+  color: #606266;
+  line-height: 1.6;
+}
+
+.msg-stats {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 12px;
+  padding: 12px;
+  background: #f0f9eb;
+  border-radius: 8px;
+}
+
+.stat-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.stat-label {
+  font-size: 12px;
+  color: #909399;
+  margin-bottom: 4px;
+}
+
+.stat-value {
+  font-size: 20px;
+  font-weight: 600;
+  color: #67c23a;
+}
+
+.stat-value.percent {
+  color: #e6a23c;
+}
+
+.msg-time {
+  font-size: 12px;
+  color: #909399;
+  display: flex;
+  align-items: center;
+}
+
+.msg-time i {
+  margin-right: 4px;
+}
+
+.dialog-footer {
+  text-align: center;
+  padding: 10px 20px 20px;
+}
+
+.dialog-footer .el-button {
+  min-width: 100px;
+}
+
+.dialog-footer .el-button i {
+  margin-right: 4px;
+}
+</style>
+
+<style>
+.order-audit-msg-dialog .el-dialog__header {
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  padding: 16px 20px;
+}
+
+.order-audit-msg-dialog .el-dialog__title {
+  color: #fff;
+  font-size: 16px;
+  font-weight: 600;
+}
+
+.order-audit-msg-dialog .el-dialog__headerbtn .el-dialog__close {
+  color: #fff;
+  font-size: 18px;
+}
+
+.order-audit-msg-dialog .el-dialog__body {
+  padding: 0;
+}
+</style>

+ 233 - 0
src/components/RedPacketBalanceMsgDialog/index.vue

@@ -0,0 +1,233 @@
+<template>
+  <el-dialog
+    title="红包余额不足提醒"
+    :visible.sync="visible"
+    width="480px"
+    :close-on-click-modal="false"
+    :show-close="true"
+    custom-class="red-packet-balance-msg-dialog"
+    @close="handleClose"
+    center
+  >
+    <div class="msg-content">
+      <div class="msg-icon warning">
+        <i class="el-icon-warning"></i>
+      </div>
+      <div class="msg-text">
+        <h3>{{ msgData.title }}</h3>
+        <p>{{ msgData.content }}</p>
+        <div class="msg-stats">
+          <div class="stat-item">
+            <span class="stat-label">未处理消息</span>
+            <span class="stat-value">{{ unreadCount }}条</span>
+          </div>
+          <div class="stat-item">
+            <span class="stat-label">未处理率</span>
+            <span class="stat-value percent">{{ unreadPercent }}%</span>
+          </div>
+        </div>
+        <div class="msg-time" v-if="msgData.createTime">
+          <i class="el-icon-time"></i>
+          {{ msgData.createTime }}
+        </div>
+      </div>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="handleKnow" size="medium">
+        <i class="el-icon-check"></i> 知道了
+      </el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import { getLatestUnreadMsg } from '@/api/crm/redPacketBalanceMsg'
+
+export default {
+  name: 'RedPacketBalanceMsgDialog',
+  data() {
+    return {
+      visible: false,
+      msgData: {
+        msgId: null,
+        title: '',
+        content: '',
+        actionUrl: '',
+        createTime: ''
+      },
+      unreadCount: 0,
+      totalCount: 0
+    }
+  },
+  computed: {
+    unreadPercent() {
+      if (this.totalCount === 0) return 0
+      return Math.round((this.unreadCount / this.totalCount) * 100)
+    }
+  },
+  methods: {
+    checkNewMsg() {
+      getLatestUnreadMsg().then(res => {
+        if (res.code === 200 && res.data) {
+          if (res.data.msg) {
+            this.msgData = res.data.msg
+            this.unreadCount = res.data.unreadCount || 0
+            this.totalCount = res.data.totalCount || 0
+            this.visible = true
+          }
+        }
+      }).catch(err => {
+        console.error('获取红包余额不足消息失败:', err)
+      })
+    },
+    handleKnow() {
+      this.visible = false
+    },
+    handleClose() {
+      this.visible = false
+    },
+    show() {
+      this.checkNewMsg()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.red-packet-balance-msg-dialog {
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.msg-content {
+  display: flex;
+  align-items: flex-start;
+  padding: 24px 16px;
+}
+
+.msg-icon {
+  flex-shrink: 0;
+  width: 56px;
+  height: 56px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 20px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.msg-icon.warning {
+  background: linear-gradient(135deg, #f5af19 0%, #f12711 100%);
+}
+
+.msg-icon i {
+  font-size: 28px;
+  color: #fff;
+  animation: pulse 1.5s ease-in-out infinite;
+}
+
+@keyframes pulse {
+  0%, 100% {
+    transform: scale(1);
+  }
+  50% {
+    transform: scale(1.1);
+  }
+}
+
+.msg-text {
+  flex: 1;
+}
+
+.msg-text h3 {
+  margin: 0 0 12px 0;
+  font-size: 18px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.msg-text p {
+  margin: 0 0 12px 0;
+  font-size: 14px;
+  color: #606266;
+  line-height: 1.6;
+}
+
+.msg-stats {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 12px;
+  padding: 12px;
+  background: #fef0f0;
+  border-radius: 8px;
+}
+
+.stat-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.stat-label {
+  font-size: 12px;
+  color: #909399;
+  margin-bottom: 4px;
+}
+
+.stat-value {
+  font-size: 20px;
+  font-weight: 600;
+  color: #f56c6c;
+}
+
+.stat-value.percent {
+  color: #e6a23c;
+}
+
+.msg-time {
+  font-size: 12px;
+  color: #909399;
+  display: flex;
+  align-items: center;
+}
+
+.msg-time i {
+  margin-right: 4px;
+}
+
+.dialog-footer {
+  text-align: center;
+  padding: 10px 20px 20px;
+}
+
+.dialog-footer .el-button {
+  min-width: 100px;
+}
+
+.dialog-footer .el-button i {
+  margin-right: 4px;
+}
+</style>
+
+<style>
+.red-packet-balance-msg-dialog .el-dialog__header {
+  background: linear-gradient(135deg, #f5af19 0%, #f12711 100%);
+  padding: 16px 20px;
+}
+
+.red-packet-balance-msg-dialog .el-dialog__title {
+  color: #fff;
+  font-size: 16px;
+  font-weight: 600;
+}
+
+.red-packet-balance-msg-dialog .el-dialog__headerbtn .el-dialog__close {
+  color: #fff;
+  font-size: 18px;
+}
+
+.red-packet-balance-msg-dialog .el-dialog__body {
+  padding: 0;
+}
+</style>

+ 24 - 20
src/layout/components/Navbar.vue

@@ -56,6 +56,9 @@
           <msg    ref="msg"  @update-count="getMsgCount" />
       </el-drawer>
 
+    <red-packet-balance-msg-dialog ref="redPacketBalanceMsgDialog" @msg-read="getMsgCount" />
+    <order-audit-msg-dialog ref="orderAuditMsgDialog" @msg-read="getMsgCount" />
+
   </div>
 </template>
 
@@ -70,6 +73,8 @@ import Search from '@/components/HeaderSearch'
 
 import msg from "@/views/crm/components/msg";
 import { getMsg,getMsgList,getMsgCount,setRead } from "@/api/crm/msg";
+import RedPacketBalanceMsgDialog from '@/components/RedPacketBalanceMsgDialog'
+import OrderAuditMsgDialog from '@/components/OrderAuditMsgDialog'
 
 export default {
   components: {
@@ -79,7 +84,9 @@ export default {
     Screenfull,
     SizeSelect,
     Search,
-    msg
+    msg,
+    RedPacketBalanceMsgDialog,
+    OrderAuditMsgDialog
   },
   computed: {
     ...mapGetters([
@@ -107,34 +114,31 @@ export default {
   data() {
     return {
       msgCount:0,
-      previousMsgCount: 0, // 保存上一次的消息计数
+      previousMsgCount: 0,
       msg:{
         open:false,
         title:'通知消息'
       },
-      msgPollingTimer: null, // 轮询定时器
+      msgPollingTimer: null,
+      isFirstCheck: true,
     }
   },
   created() {
     this.getMsgCount();
-    // 启动消息轮询,每30秒检查一次新消息
     this.startMsgPolling();
   },
   beforeDestroy() {
-    // 组件销毁前清除定时器
     this.stopMsgPolling();
   },
   methods: {
     startMsgPolling() {
-      // 清除已存在的定时器
       if (this.msgPollingTimer) {
         clearInterval(this.msgPollingTimer);
       }
 
-      // 设置定时器,每30秒获取一次消息计数
       this.msgPollingTimer = setInterval(() => {
         this.getMsgCount();
-      }, 30000); // 30秒轮询一次
+      }, 30000);
     },
     stopMsgPolling() {
       if (this.msgPollingTimer) {
@@ -142,26 +146,28 @@ export default {
         this.msgPollingTimer = null;
       }
     },
+    checkSpecialMsgs() {
+      if (this.$refs.redPacketBalanceMsgDialog) {
+        this.$refs.redPacketBalanceMsgDialog.show();
+      }
+      if (this.$refs.orderAuditMsgDialog) {
+        this.$refs.orderAuditMsgDialog.show();
+      }
+    },
     getMsgCount(){
       getMsg().then(response => {
-        // 计算所有未读消息总数
         let totalCount = 0;
         response.counts.forEach(item => {
           totalCount += item.total;
         });
 
-        // 保存旧的消息计数
         this.previousMsgCount = this.msgCount;
-        // 更新消息计数
         this.msgCount = totalCount;
 
-        // 如果消息计数增加了,显示通知弹框
-        if (this.msgCount > this.previousMsgCount && this.msgCount > 0) {
-          this.$notify({
-            title: '提示',
-            message: '您有'+this.msgCount+"条消息通知",
-            position: 'top-right',
-            type: 'info'
+        if (this.isFirstCheck) {
+          this.isFirstCheck = false;
+          this.$nextTick(() => {
+            this.checkSpecialMsgs();
           });
         }
 
@@ -172,11 +178,9 @@ export default {
     openMsg(){
       console.log(11);
       this.msg.open=true;
-      // 每次打开消息界面时刷新数据
       if (this.$refs.msg) {
         this.$refs.msg.getMsg();
       }
-      // 打开消息后重新获取消息计数来更新角标
       this.getMsgCount();
     },
     toggleSideBar() {