lrf hai 8 meses
pai
achega
f4f326ccbd

+ 11 - 0
package-lock.json

@@ -10,6 +10,7 @@
       "dependencies": {
         "@element-plus/icons-vue": "^2.3.1",
         "@kjgl77/datav-vue3": "^1.7.3",
+        "@stomp/stompjs": "^7.0.0",
         "@vueuse/core": "^10.7.2",
         "@vueuse/integrations": "^10.9.0",
         "@wangeditor/editor": "^5.1.23",
@@ -1144,6 +1145,11 @@
         "nanopop": "^2.1.0"
       }
     },
+    "node_modules/@stomp/stompjs": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/@stomp/stompjs/-/stompjs-7.0.0.tgz",
+      "integrity": "sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw=="
+    },
     "node_modules/@transloadit/prettier-bytes": {
       "version": "0.0.7",
       "resolved": "https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz",
@@ -8209,6 +8215,11 @@
         "nanopop": "^2.1.0"
       }
     },
+    "@stomp/stompjs": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/@stomp/stompjs/-/stompjs-7.0.0.tgz",
+      "integrity": "sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw=="
+    },
     "@transloadit/prettier-bytes": {
       "version": "0.0.7",
       "resolved": "https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz",

+ 1 - 0
package.json

@@ -13,6 +13,7 @@
   "dependencies": {
     "@element-plus/icons-vue": "^2.3.1",
     "@kjgl77/datav-vue3": "^1.7.3",
+    "@stomp/stompjs": "^7.0.0",
     "@vueuse/core": "^10.7.2",
     "@vueuse/integrations": "^10.9.0",
     "@wangeditor/editor": "^5.1.23",

+ 32 - 2
src/components/custom/custom-layout.vue

@@ -31,8 +31,10 @@
             <span>|</span>
             <span @click="toLogin('1')">登录</span>
           </div>
-          <div class="right_2" @click="toChat">
-            <el-icon><Bell /></el-icon>
+          <div class="right_2" @click="toChat" v-if="user">
+            <el-badge :value="notReadNum">
+              <el-icon><Bell /></el-icon>
+            </el-badge>
             <span>消息</span>
           </div>
         </div>
@@ -80,12 +82,15 @@
 </template>
 
 <script setup>
+import { initClient } from '../../utils/stomp'
 import { ArrowDown } from '@element-plus/icons-vue'
 import { cloneDeep, get } from 'lodash-es'
 import { siteInfo, footInfo, menuList2 } from '@/layout/site'
 // 接口
 import { DesignStore } from '@/store/api/platform/design'
+import { MessageStore } from '@/store/api/system/message'
 const designStore = DesignStore()
+const messageStore = MessageStore()
 import { UserStore } from '@/store/user'
 const userStore = UserStore()
 const user = computed(() => userStore.user)
@@ -104,10 +109,35 @@ const data = ref([])
 const info = ref({})
 const isIncubator = ref(false)
 const hasbrain = ref(false)
+const notReadNum = ref(0)
+const mqClient = ref()
 // 请求
 onMounted(async () => {
   search()
+  searchMessage()
 })
+
+const toSubscribe = () => {
+  const userid = get(user, 'value.id')
+  const subscribes = { [`/exchange/userEx/${userid}`]: dealMsg }
+  mqClient.value = initClient(subscribes)
+}
+const dealMsg = (data) => {
+  searchMessage()
+}
+
+const searchMessage = async () => {
+  // 没有用户id就不查了
+  if (!get(user, 'value.id')) return
+  // 只要1个数据,主要是要总数
+  const res = await messageStore.checkNotRead()
+  if ($checkRes(res)) {
+    const nrn = get(res, 'data', 0)
+    notReadNum.value = nrn
+  }
+  toSubscribe()
+}
+
 const search = async () => {
   for (const val of menuList2) {
     if (route.name === val.route) val.hover = true

+ 45 - 0
src/store/api/system/message.js

@@ -0,0 +1,45 @@
+import { defineStore } from 'pinia'
+import { AxiosWrapper } from '@/utils/axios-wrapper'
+import { get } from 'lodash-es'
+const url = '/message'
+const axios = new AxiosWrapper()
+
+export const MessageStore = defineStore('message', () => {
+  const query = async ({ skip = 0, limit = undefined, ...info } = {}) => {
+    let cond = {}
+    if (skip) cond.skip = skip
+    if (limit) cond.limit = limit
+    cond = { ...cond, ...info }
+    const res = await axios.$get(`${url}`, cond)
+    return res
+  }
+  const fetch = async (payload) => {
+    const res = await axios.$get(`${url}/${payload}`)
+    return res
+  }
+  const create = async (payload) => {
+    const res = await axios.$post(`${url}`, payload)
+    return res
+  }
+  const update = async (payload) => {
+    const id = get(payload, 'id', get(payload, 'id'))
+    const res = await axios.$post(`${url}/${id}`, payload)
+    return res
+  }
+  const del = async (payload) => {
+    const res = await axios.$delete(`${url}/${payload}`)
+    return res
+  }
+  const checkNotRead = async (payload) => {
+    const res = await axios.$get(`${url}/checkNotRead`)
+    return res
+  }
+  return {
+    query,
+    fetch,
+    create,
+    update,
+    del,
+    checkNotRead
+  }
+})

+ 42 - 0
src/utils/stomp.js

@@ -0,0 +1,42 @@
+import { Client } from '@stomp/stompjs'
+import { isObject, get } from 'lodash-es'
+let client
+const options = {
+  brokerURL: 'ws://localhost:15674/ws',
+  connectHeaders: {
+    host: 'hxmsg',
+    login: 'huaxin',
+    passcode: '1234qwerasdf'
+  },
+  reconnectDelay: 5000,
+  heartbeatIncoming: 4000,
+  heartbeatOutgoing: 4000
+}
+/**
+ * 初始化stomp并订阅指定地址
+ * @param {Object} subscribes 订阅配置 {[url]:(msg)=>{}} key:订阅地址;value:回调函数
+ * @returns
+ */
+const initClient = (subscribes) => {
+  client = new Client(options)
+  client.onConnect = (res) => {
+    console.log('is connected')
+    if (!isObject(subscribes)) return
+    for (const url in subscribes) {
+      const cb = subscribes[url]
+      client.subscribe(url, (msg) => {
+        let body = get(msg, 'body')
+        if (body) body = JSON.parse(body)
+        cb(body)
+      })
+    }
+  }
+  client.onStompError = (res) => {
+    console.log('connect error')
+    console.log(res)
+  }
+  client.activate()
+  return client
+}
+
+export { initClient }

+ 54 - 16
src/views/center/notice.vue

@@ -7,9 +7,18 @@
             <template #empty>
               <el-empty description="暂无数据" />
             </template>
-            <el-table-column prop="type" align="center" label="通知种类" width="180" />
-            <el-table-column prop="title" align="center" label="标题" />
-            <el-table-column prop="time" align="center" label="时间" width="180" />
+            <el-table-column prop="type" align="center" label="通知类型" width="100">
+              <template #="{ row }">
+                {{ getDict(row, 'type') }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="content" align="center" label="内容" />
+            <el-table-column prop="is_read" align="center" label="是否已读" width="100">
+              <template #="{ row }">
+                {{ getDict(row, 'is_read') }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="created_time" align="center" label="时间" width="200" />
           </el-table>
         </el-col>
         <el-col :span="24" class="two">
@@ -21,8 +30,14 @@
 </template>
 
 <script setup>
+import { MessageStore } from '@/store/api/system/message'
+import { DictDataStore } from '@/store/api/system/dictData'
+import { get } from 'lodash-es'
 import { UserStore } from '@/store/user'
+const $checkRes = inject('$checkRes')
 const userStore = UserStore()
+const store = MessageStore()
+const dictDataStore = DictDataStore()
 const user = computed(() => userStore.user)
 // 加载中
 const loading = ref(false)
@@ -42,20 +57,43 @@ onMounted(async () => {
   loading.value = false
 })
 const search = async (query = { skip, limit }) => {
-  // skip = query.skip
-  // limit = query.limit
-  // const info = {
-  //   skip: query.skip,
-  //   limit: query.limit,
-  //   user: user.value.id
-  // }
-  // const res = await store.query(info)
-  // if (res.errcode == '0') {
-  //   list.value = res.data
-  //   total.value = res.total
-  // }
+  skip = query.skip
+  limit = query.limit
+  const info = {
+    skip: query.skip,
+    limit: query.limit,
+    'to.user': get(user, 'value.id')
+  }
+  const res = await store.query(info)
+  if (res.errcode == '0') {
+    list.value = res.data
+    total.value = res.total
+  }
+}
+const isReadList = ref([])
+const msgTypeList = ref([])
+const searchOther = async () => {
+  let result = await dictDataStore.query({ code: 'messageIsRead', is_use: '0' })
+  if ($checkRes(result)) isReadList.value = result.data
+  result = await dictDataStore.query({ code: 'messageType', is_use: '0' })
+  if ($checkRes(result)) msgTypeList.value = result.data
+}
+const getDict = (data, type) => {
+  if (type === 'type') {
+    const i = msgTypeList.value.find((f) => f.value === get(data, 'type'))
+    if (i) return get(i, 'label')
+  } else if (type === 'is_read') {
+    //理论上,to中应该只有这个数据,但是还要处理
+    const userid = get(user, 'value.id')
+    if (!userid) return
+    const to = get(data, 'to', [])
+    const is_readData = to.find((f) => f.user === userid)
+    if (!is_readData) return
+    const is_read = get(is_readData, 'is_read', '0')
+    const d = isReadList.value.find((f) => f.value === is_read)
+    if (d) return get(d, 'label')
+  }
 }
-const searchOther = async () => {}
 // 分页
 const changePage = (page = currentPage.value) => {
   search({ skip: (page - 1) * limit, limit: limit })