Browse Source

feat(dependencies): 添加 event-source-polyfill 依赖

- 添加 event-source-polyfill 依赖以支持旧浏览器的 Server-Sent Events 功能。
- 更新 Message.vue 和 Message1.vue 组件,使用 EventSourcePolyfill 替代原生 EventSource。
- 移除 Message.vue 和 Message1.vue 中的 fetch 请求创建 EventSource 的逻辑。
- 新增 Message2.vue 组件,用于展示消息列表。
luoyali 9 tháng trước cách đây
mục cha
commit
f504749c17

+ 1 - 0
package.json

@@ -44,6 +44,7 @@
     "dayjs": "^1.11.7",
     "echarts": "^5.4.2",
     "element-plus": "2.3.4",
+    "event-source-polyfill": "^1.0.31",
     "everright-filter": "^1.1.1",
     "html2canvas": "^1.4.1",
     "js-md5": "^0.7.3",

+ 33 - 38
src/layout/components/Header/components/Message.vue

@@ -16,55 +16,50 @@
 		</ul>
 	</el-popover>
 </template>
+
 <script setup>
-import { onMounted, ref, onBeforeUnmount } from 'vue'
+import { onMounted, onBeforeUnmount, ref } from 'vue'
+import { EventSourcePolyfill } from 'event-source-polyfill'
 import { ls } from '@/utils'
+import { ElMessageBox, ElNotification } from 'element-plus'
 const token = ls.get('token')
 const { VITE_APP_BASE_API } = import.meta.env
-const serviceUrlApi = ref(`${VITE_APP_BASE_API}/sys/sse/connect`);
-// const serviceUrlApi = ref(`http://localhost:3000/events`)
-let sseService = ref(null)
-let receivedData = ref([])
+const serviceUrlApi = ref(`${VITE_APP_BASE_API}/sys/sse/connect`)
+const receivedData = ref([])
+let eventSource = null
 
-// 创建一个新的 EventSource,但首先使用 fetch API 发送带有 Authorization 头的请求
-function connectSSEWithHeaders(url, headers = {}) {
-	return fetch(url, {
-		method: 'GET',
-		headers: headers,
-		cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
-		mode: 'cors', // no-cors, cors, *same-origin
-		credentials: 'same-origin', // include, *same-origin, omit
-		redirect: 'follow', // manual, *follow, error
-		referrerPolicy: 'no-referrer' // no-referrer, *client
+onMounted(() => {
+	if (!token) {
+		return ElMessageBox.alert('当前页面已失效,请重新登录', '提示', {})
+	}
+	eventSource = new EventSourcePolyfill(serviceUrlApi.value, {
+		headers: { accessToken: token }
 	})
-		.then(response => {
-			if (!response.ok) {
-				throw new Error('网络响应失败')
-			}
-			return new EventSource(url)
-		})
-		.catch(error => {
-			console.error('There was a problem with your fetch operation:', error)
+
+	eventSource.onmessage = event => {
+		console.log('Received message:', event.data)
+		receivedData.value.push(event.data)
+		// 处理接收到的消息
+		ElNotification({
+			title: '您有一条新消息',
+			message: event.data,
+			type: 'success'
 		})
-}
+	}
 
-onMounted(() => {
-	connectSSEWithHeaders(serviceUrlApi.value, { accessToken: token }).then(eventSource => {
-		sseService.value = eventSource
-		eventSource.onmessage = function (e) {
-			console.log('后端返回的数据:', e.data)
-			receivedData.value.push(e.data)
-		}
-		eventSource.onerror = function (err) {
-			console.error('EventSource failed:', err)
-		}
-	})
+	eventSource.onerror = error => {
+		console.error('EventSource failed:', error)
+		ElMessageBox.alert('当前通讯连接已失效,请重新登录', '提示', {})
+	}
 })
 
 onBeforeUnmount(() => {
-	if (sseService.value) {
-		sseService.value.close()
-		sseService.value = null
+	if (eventSource) {
+		eventSource.close()
 	}
 })
 </script>
+
+<style scoped>
+/* 你的样式代码 */
+</style>

+ 26 - 39
src/layout/components/Header/components/Message1.vue

@@ -12,18 +12,18 @@
 			</div>
 		</template>
 		<ul>
-			<li v-for="(item, idx) in receivedData" :key="idx"> {{ item }}</li>
+			<li v-for="(item, idx) in receivedData" :key="idx">{{ item }}</li>
 		</ul>
 	</el-popover>
 </template>
 <script setup>
-import { onMounted, ref, onBeforeUnmount } from 'vue';
-import {ls} from "@/utils";
+import { onMounted, ref, onBeforeUnmount } from 'vue'
+import { ls } from '@/utils'
 const token = ls.get('token')
-const { VITE_APP_BASE_API } = import.meta.env;
+const { VITE_APP_BASE_API } = import.meta.env
 // const serviceUrlApi = ref(`${VITE_APP_BASE_API}/sys/sse/connect`);
-const serviceUrlApi = ref(`http://localhost:3000/events`);
-let sseService = ref(null);
+const serviceUrlApi = ref(`http://localhost:3000/events`)
+let sseService = ref(null)
 let receivedData = ref([])
 
 // 创建一个新的 EventSource,但首先使用 fetch API 发送带有 Authorization 头的请求
@@ -35,49 +35,36 @@ function connectSSEWithHeaders(url, headers = {}) {
 		mode: 'cors', // no-cors, cors, *same-origin
 		credentials: 'same-origin', // include, *same-origin, omit
 		redirect: 'follow', // manual, *follow, error
-		referrerPolicy: 'no-referrer', // no-referrer, *client
+		referrerPolicy: 'no-referrer' // no-referrer, *client
 	})
 		.then(response => {
 			if (!response.ok) {
-				throw new Error('Network response was not ok');
+				throw new Error('Network response was not ok')
 			}
-			return new EventSource(url);
-			// 读取响应体作为文本,然后解析URL来创建 EventSource
-			// const t = response.json()
-			// console.log(t);
-			// return response.text().then(text => {
-			//      console.info('text', text)
-			//       // 这里通常不需要解析text,因为我们只是要确认连接成功。
-			//       // 但如果SSE URL在响应体中,你可能需要处理它。
-			//       // 假设服务器直接支持SSE在原始URL
-			//
-			//   });
+			return new EventSource(url)
 		})
 		.catch(error => {
-			console.error('There was a problem with your fetch operation:', error);
-		});
+			console.error('There was a problem with your fetch operation:', error)
+		})
 }
 
-
 onMounted(() => {
-	connectSSEWithHeaders(serviceUrlApi.value,  { 'accessToken': token })
-		.then(eventSource => {
-			sseService = eventSource
-			eventSource.onmessage = function(e) {
-				console.log(e, '----====----');
-				console.log('后端返回的数据:', e.data);
-				receivedData.value.push(e.data)
-			}
-			eventSource.onerror = function(err) {
-				console.error('EventSource failed:', err);
-			};
-		})
+	connectSSEWithHeaders(serviceUrlApi.value, { accessToken: token }).then(eventSource => {
+		sseService.value = eventSource
+		eventSource.onmessage = function (e) {
+			console.log('后端返回的数据:', e.data)
+			receivedData.value.push(e.data)
+		}
+		eventSource.onerror = function (err) {
+			console.error('EventSource failed:', err)
+		}
+	})
 })
 
 onBeforeUnmount(() => {
-	if (sseService) {
-		sseService.close();
-		sseService = null;
+	if (sseService.value) {
+		sseService.value.close()
+		sseService.value = null
 	}
-});
-</script>
+})
+</script>

+ 145 - 0
src/layout/components/Header/components/Message2.vue

@@ -0,0 +1,145 @@
+<template>
+	<el-popover placement="bottom" :width="310" trigger="click">
+		<template #reference>
+			<div class="menu--message-trigger">
+				<el-tooltip :content="$t('le.message.txt')" effect="dark" placement="bottom">
+					<div class="menu--message menu-item le-hover-effect--bg">
+						<el-badge :value="total" :max="99">
+							<i class="le-iconfont le-notice"></i>
+						</el-badge>
+					</div>
+				</el-tooltip>
+			</div>
+		</template>
+		<el-tabs class="message-tabs" v-model="activeTab">
+			<el-tab-pane v-for="v of tabsConfig" :key="v.name" :name="v.name">
+				<template #label> {{ v.label }}({{ v.list.length }}) </template>
+				<template v-if="v.list.length">
+					<div class="message-list">
+						<div v-for="item of v.list" :key="item.id" class="message-item" @click="jumpMessageDetail(item.id)">
+							<!--<img src="" alt="" class="message-icon" />-->
+							<div class="message-content">
+								<div class="message-title">
+									<span class="txt text-overflow_ellipsis" :title="item.title">{{ item.title }}</span>
+									<span class="message-date">{{ item.createTime }}</span>
+								</div>
+								<span class="message-txt">{{ item.content }}</span>
+							</div>
+						</div>
+						<div class="message-fix-item" @click="jumpMessageInfo">查看更多</div>
+					</div>
+				</template>
+				<template v-else>
+					<LeNoData />
+				</template>
+			</el-tab-pane>
+		</el-tabs>
+	</el-popover>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive } from 'vue'
+import message from '@/api/system/message'
+import router from '@/router'
+// noticeList  通知     messageList   消息     todoList   待办
+const tabsConfig = reactive({
+	noticeList: {
+		name: 'noticeList',
+		label: '通知',
+		list: []
+	},
+	messageList: {
+		name: 'messageList',
+		label: '消息',
+		list: []
+	},
+	todoList: {
+		name: 'todoList',
+		label: '待办',
+		list: []
+	}
+})
+const activeTab = ref('noticeList')
+const total = ref(0)
+message.getMessage().then(res => {
+	let _total = 0
+	Object.keys(tabsConfig).map(key => {
+		if (res[key]) {
+			tabsConfig[key].list = res[key]
+			_total += res[key].length
+		} else {
+			res[key].list = []
+		}
+	})
+	total.value = _total
+})
+
+const jumpMessageInfo = () => {
+	router.push('/message/list')
+}
+
+const jumpMessageDetail = (id: any) => {
+	router.push('/message/list?id=' + id)
+}
+</script>
+
+<style scoped lang="scss">
+.message-list {
+	display: flex;
+	flex-direction: column;
+	max-height: 390px;
+	overflow-y: auto;
+	.message-item {
+		display: flex;
+		align-items: center;
+		padding: 8px 0;
+		border-bottom: 1px solid var(--el-border-color-light);
+		cursor: pointer;
+		&:last-child {
+			border: none;
+		}
+		.message-content {
+			display: flex;
+			flex-direction: column;
+			width: 100%;
+			.message-title {
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				margin-bottom: 5px;
+				font-weight: 600;
+				color: var(--el-text-color-primary);
+			}
+			.message-date {
+				font-size: 12px;
+				color: var(--el-text-color-secondary);
+				margin-left: 10px;
+				font-weight: 400;
+				flex-shrink: 0;
+			}
+			.message-txt {
+				//margin-bottom: 5px;
+				color: var(--el-text-color-regular);
+			}
+		}
+	}
+	.message-fix-item {
+		// flex 居中
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 40px;
+		cursor: pointer;
+	}
+}
+.menu--message-trigger {
+	height: 100%;
+}
+.menu--message {
+	width: 40px;
+	justify-content: center;
+	//.le-notice {
+	//.le-iconfont {
+	//}
+}
+</style>