修改websocket

This commit is contained in:
lccsw
2025-11-18 16:59:23 +08:00
parent 501815d7ac
commit a6c90aaab0
15 changed files with 980 additions and 47 deletions
+3
View File
@@ -1,2 +1,5 @@
VUE_APP_NAME=超级车补管理后台
VUE_APP_API_BASE_URL=https://hcb.liche.cn/pingan
# webSocket配置
VUE_APP_WS_URL=wss://api.ss.haodian.cn/wss
VUE_APP_WS_PLATFORM=2
+3
View File
@@ -1,3 +1,6 @@
VUE_APP_API_BASE_URL=/pingan
URL = http://agent.admin.haodian.cn
PORT = 8200
# webSocket配置
VUE_APP_WS_URL=ws://api.ss.haodian.cn/wss
VUE_APP_WS_PLATFORM=2
+38
View File
@@ -0,0 +1,38 @@
import request from '@/utils/request';
/**
* 通知列表
* @param params 查询条件
*/
export async function pageNotice(params) {
const res = await request.get('/user/notice/page', {
params
});
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 获取未读消息数
* @param params
* @returns {Promise<*>}
*/
export async function unRead(params) {
const res = await request.get('/user/notice/unRead', {
params
});
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
export async function setRead(data) {
const res = await request.post('/user/notice/read', data);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
+3
View File
@@ -63,3 +63,6 @@ export const MAP_KEY = '006d995d433058322319fa797f2876f5';
// EleAdmin 授权码, 自带的只能用于演示, 正式项目请更换为自己的授权码
export const LICENSE_CODE =
'dk9mcwJyetRWQlxWRiojIqJWdzJCLi4Wa4QDN5ojI0NWZI1kI6ICZpJCLiwiIVNzVz4UW6Iibvl2cyVmdQfiETMuEjI0NW==';
// token 存储的名称
export const WEB_SOCKET_TOKEN_STORE_NAME = 'websocket_access_token';
+75 -41
View File
@@ -10,13 +10,16 @@
>
<template v-slot:reference>
<div class="ele-notice-group">
<el-badge :value="unreadNum" :hidden="!unreadNum">
<el-badge :value="noticeCount" :hidden="!noticeCount">
<i class="el-icon-bell"></i>
</el-badge>
</div>
</template>
<el-tabs v-model="active">
<el-tab-pane name="notice" :label="noticeTitle">
<el-tab-pane
name="notice"
:label="'通知' + [noticeCount ? '(' + noticeCount + ')' : '']"
>
<div class="ele-notice-list ele-scrollbar-mini">
<div
v-for="(item, index) in notice"
@@ -26,8 +29,8 @@
<div class="ele-cell ele-notice-item-wrapper">
<i :class="[item.icon, 'ele-notice-item-icon']"></i>
<div class="ele-cell-content">
<div class="ele-elip">{{ item.title }}</div>
<div class="ele-text-secondary ele-elip">{{ item.time }}</div>
<div class="ele-elip">{{ item.content }}</div>
<div class="ele-text-secondary ele-elip">{{ item.c_time }}</div>
</div>
</div>
<el-divider />
@@ -36,12 +39,13 @@
<div v-if="notice.length" class="ele-cell ele-notice-actions">
<div class="ele-cell-content" @click="clearNotice">清空通知</div>
<el-divider direction="vertical" class="line-color-light" />
<router-link to="/user/message?type=notice" class="ele-cell-content">
<router-link to="/user/notice?type=notice" class="ele-cell-content">
查看更多
</router-link>
</div>
<ele-empty v-if="!notice.length" text="已查看所有通知" />
</el-tab-pane>
<!--
<el-tab-pane name="letter" :label="letterTitle">
<div class="ele-notice-list ele-scrollbar-mini">
<div
@@ -101,12 +105,14 @@
</div>
<ele-empty v-if="!todo.length" text="已完成所有任务" />
</el-tab-pane>
-->
</el-tabs>
</el-popover>
</template>
<script>
import { getUnreadNotice } from '@/api/layout';
import { pageNotice, setRead } from '@/api/user/notice';
// import websocket from '@/utils/websocket';
export default {
data() {
@@ -117,58 +123,86 @@
active: 'notice',
// 通知数据
notice: [],
noticeCount: 0,
// 私信数据
letter: [],
letterCount: 0,
// 待办数据
todo: []
todo: [],
todCount: 0
};
},
computed: {
// 通知标题
noticeTitle() {
return '通知' + (this.notice.length ? `(${this.notice.length})` : '');
},
// 私信标题
letterTitle() {
return '私信' + (this.letter.length ? `(${this.letter.length})` : '');
},
// 待办标题
todoTitle() {
return '待办' + (this.todo.length ? `(${this.todo.length})` : '');
},
// 未读数量
unreadNum() {
return this.notice.length + this.letter.length + this.todo.length;
}
mounted() {
// 监听 WebSocket 连接状态
window.EventBus.$on('ws:connected', (status) => {
console.log('WebSocket 连接状态:', status);
});
// 监听 WebSocket 消息
window.EventBus.$on('ws:message', (message) => {
console.log('收到 WebSocket 消息:', message);
if (message.type === 'notice') {
//弹窗通知有新消息
this.$notify({
title: '', // 消息标题
message: '您收到一条新的系统通知,请及时查看', // 消息内容
type: 'warning', // 类型:info/success/warning/error
duration: 5000, // 5秒后自动关闭,0则不自动关闭
position: 'top-left', // 弹窗位置,可选top-left/bottom-left/bottom-right
showClose: true, // 显示关闭按钮
onClick: () => {}
});
//怎么在最开头拼接
this.notice = [message.data].concat(this.notice);
this.noticeCount++;
// this.notice = message.data;
}
// 处理具体的业务逻辑
// this.handleWebSocketMessage(message);
});
// 监听 WebSocket 错误
window.EventBus.$on('ws:error', (error) => {
console.error('WebSocket 错误:', error);
});
},
computed: {},
created() {
this.query();
},
methods: {
/* 查询数据 */
query() {
getUnreadNotice()
.then((result) => {
this.notice = result.notice;
this.letter = result.letter;
this.todo = result.todo;
// getUnreadNotice()
// .then((result) => {
// this.notice = result.notice;
// this.letter = result.letter;
// this.todo = result.todo;
// })
// .catch((e) => {
// this.$message.error(e.message);
// });
pageNotice({ read: 0 }).then((result) => {
this.notice = result.list;
this.noticeCount = result.count;
});
},
/* 清空通知 */
clearNotice() {
setRead({ type: 'all' })
.then(() => {
this.query();
})
.catch((e) => {
this.$message.error(e.message);
});
},
/* 清空通知 */
clearNotice() {
this.notice = [];
},
/* 清空通知 */
clearLetter() {
this.letter = [];
},
/* 清空通知 */
clearTodo() {
this.todo = [];
}
},
beforeDestroy() {
// 组件销毁前移除事件监听
window.EventBus.$off('ws:connected');
window.EventBus.$off('ws:message');
window.EventBus.$off('ws:error');
}
};
</script>
@@ -16,11 +16,9 @@
</div>
-->
<!-- 消息通知 -->
<!--
<div class="ele-admin-header-tool-item">
<header-notice />
</div>
-->
<!-- 用户信息 -->
<div class="ele-admin-header-tool-item">
<el-dropdown @command="onUserDropClick">
@@ -59,7 +57,7 @@
</template>
<script>
// import HeaderNotice from './header-notice.vue';
import HeaderNotice from './header-notice.vue';
// import PasswordModal from './password-modal.vue';
import SettingDrawer from './setting-drawer.vue';
// import I18nIcon from './i18n-icon.vue';
@@ -67,7 +65,7 @@
export default {
// components: { HeaderNotice, PasswordModal, SettingDrawer, I18nIcon },
components: { SettingDrawer },
components: { SettingDrawer, HeaderNotice },
props: {
// 是否是全屏
fullscreen: Boolean
+12 -1
View File
@@ -8,6 +8,13 @@ import EleAdmin from 'ele-admin';
import VueClipboard from 'vue-clipboard2';
import i18n from './i18n';
import './styles/index.scss';
import websocket from '@/utils/websocket';
// 初始化事件总线(跨组件通信)
Vue.prototype.$eventBus = new Vue();
window.EventBus = new Vue();
// 应用启动时自动检测 Token 并初始化 WebSocket
websocket.init();
Vue.config.productionTip = false;
@@ -26,5 +33,9 @@ new Vue({
router,
store,
i18n,
render: (h) => h(App)
render: (h) => h(App),
beforeDestroy() {
// 应用销毁时关闭连接
this.$ws.close();
}
}).$mount('#app');
+4
View File
@@ -8,6 +8,7 @@ import { WHITE_LIST, REDIRECT_PATH, LAYOUT_PATH } from '@/config/setting';
import store from '@/store';
import { getToken } from '@/utils/token-util';
import { routes, getMenuRoutes } from './routes';
import websocket from '@/utils/websocket';
Vue.use(VueRouter);
@@ -44,6 +45,9 @@ router.beforeEach((to, from, next) => {
});
} else {
next();
if (!websocket.isConnected) {
websocket.init();
}
}
} else if (WHITE_LIST.includes(to.path)) {
next();
+15 -1
View File
@@ -1,7 +1,10 @@
/**
* token 操作封装
*/
import { TOKEN_STORE_NAME } from '@/config/setting';
import {
TOKEN_STORE_NAME,
WEB_SOCKET_TOKEN_STORE_NAME
} from '@/config/setting';
/**
* 获取缓存的 token
@@ -37,3 +40,14 @@ export function removeToken() {
localStorage.removeItem(TOKEN_STORE_NAME);
sessionStorage.removeItem(TOKEN_STORE_NAME);
}
export function setWebSocketToken(token) {
sessionStorage.setItem(WEB_SOCKET_TOKEN_STORE_NAME, token);
}
export function getWebSocketToken() {
return sessionStorage.getItem(WEB_SOCKET_TOKEN_STORE_NAME);
}
export function removeWebSocketToken() {
sessionStorage.removeItem(WEB_SOCKET_TOKEN_STORE_NAME);
}
+20
View File
@@ -0,0 +1,20 @@
/**
* 判断字符串是否为有效的 JSON
* @param {string} str - 需要判断的字符串
* @returns {boolean} 是有效 JSON 则返回 true,否则返回 false
*/
export function isJson(str) {
// 先排除非字符串的情况
if (typeof str !== 'string') {
return false;
}
try {
// 尝试解析 JSON
const obj = JSON.parse(str);
// 解析成功后,需确认结果是对象或数组(避免纯字符串/数字被误判)
return typeof obj === 'object' && obj !== null;
} catch (e) {
// 解析失败,不是有效 JSON
return false;
}
}
+170
View File
@@ -0,0 +1,170 @@
import { getToken } from '@/utils/token-util';
class WebSocketService {
constructor() {
this.ws = null; // 连接实例
this.isConnected = false; // 连接状态
this.isConnecting = false; // 添加此状态
this.reconnectTimer = null; // 重连计时器
this.heartbeatTimer = null; // 心跳计时器
this.heartbeatInterval = 30000; // 心跳间隔(30秒)
this.maxReconnectCount = 100; // 最大重连次数
this.reconnectCount = 0; // 当前重连次数
}
// 初始化连接(核心方法)
init() {
// 更严格的连接状态检查
if (
this.isConnecting ||
(this.ws && this.ws.readyState === WebSocket.CONNECTING)
) {
console.log('连接已在进行中,跳过重复连接请求');
return false;
}
// 检查是否已经建立连接
if (this.isConnected && this.ws && this.ws.readyState === WebSocket.OPEN) {
console.log('WebSocket 已连接,无需重复连接');
return false;
}
// 1. 获取 Token 和用户 ID(优先 Vuex,其次 localStorage
let token = getToken();
// 2. 无 Token 则阻断连接,等待登录
if (!token) {
console.log('无有效 Token,等待登录后连接');
return false;
}
this.isConnecting = true;
// 3. 已存在连接则关闭旧连接
if (this.ws) this.close();
// 4. 建立 WebSocket 连接(携带授权信息)
const wsUrl = `${process.env.VUE_APP_WS_URL}?token=${token}&platform=${process.env.VUE_APP_WS_PLATFORM}`;
this.ws = new WebSocket(wsUrl);
// 5. 连接成功回调
this.ws.onopen = () => {
console.log('WebSocket 连接成功');
this.isConnected = true;
this.reconnectCount = 0; // 重置重连次数
this.startHeartbeat(); // 启动心跳
window.EventBus.$emit('ws:connected', true); // 触发连接成功事件
};
// 6. 接收消息回调
this.ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
// console.log('WebSocket 收到消息:', message);
// 处理 Token 失效(服务端约定 code: 401
if (message.code === 401) {
this.handleTokenInvalid();
return;
}
// 正常消息分发
window.EventBus.$emit('ws:message', message);
} catch (error) {
console.error('消息解析失败:', error);
}
};
// 7. 连接关闭回调
this.ws.onclose = (event) => {
console.log(`WebSocket 关闭(code: ${event.code}`);
// 确保重置连接状态
this.isConnected = false;
this.isConnecting = false; // 必须重置连接中状态
this.stopHeartbeat();
window.EventBus.$emit('ws:connected', false);
// 异常关闭则重连
if (event.code !== 1000 && this.reconnectCount < this.maxReconnectCount) {
this.reconnect();
}
};
// 8. 连接错误回调
this.ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
window.EventBus.$emit('ws:error', error);
};
}
// 发送消息
send(data) {
if (this.isConnected && this.ws) {
this.ws.send(JSON.stringify(data));
} else {
console.error('WebSocket 未连接,无法发送消息');
}
}
// 主动关闭连接
close() {
this.isConnecting = false; // 重置连接中状态
if (this.ws) {
this.ws.close(1000, '手动关闭'); // 1000 表示正常关闭
this.ws = null;
this.isConnected = false;
this.stopHeartbeat();
this.clearReconnectTimer();
}
}
// 心跳检测(维持连接)
startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
if (this.isConnected) {
this.send({ type: 'heartbeat', content: 'ping' });
}
}, this.heartbeatInterval);
}
// 停止心跳
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
// 重连机制
reconnect() {
this.clearReconnectTimer();
this.isConnecting = false; // 重置连接状态
this.reconnectCount++;
console.log(`${this.reconnectCount} 次重连...`);
this.reconnectTimer = setTimeout(() => {
// 重连前确保连接已完全关闭
if (this.ws) {
this.ws.onopen = null;
this.ws.onclose = null;
this.ws.onerror = null;
this.ws.onmessage = null;
this.ws = null;
}
this.init();
}, 3000 * this.reconnectCount); // 指数退避重连(3s, 6s, 9s...
}
// 清除重连计时器
clearReconnectTimer() {
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
}
// 处理 Token 失效
handleTokenInvalid() {
this.close(); // 关闭连接
// store.dispatch('user/logout'); // 清除 Token
// router.push('/login?redirect=' + router.currentRoute.path); // 跳转登录页(带回调地址)
// window.EventBus.$emit('ws:error', 'Token 已过期,请重新登录');
}
}
// 单例导出
export default new WebSocketService();
@@ -0,0 +1,149 @@
<template>
<ele-pro-table
ref="table"
:columns="columns"
:datasource="datasource"
:selection.sync="selection"
tool-class="ele-toolbar-actions"
>
<template v-slot:toolbar>
<el-button size="small" type="primary" @click="read">
标记已读
</el-button>
<el-button size="small" type="danger" @click="removeBatch">
删除消息
</el-button>
</template>
<template v-slot:status="{ row }">
<span :class="['ele-text-danger', 'ele-text-info'][row.status]">
{{ ['未读', '已读'][row.status] }}
</span>
</template>
<template v-slot:action="{ row }">
<el-link
type="primary"
:underline="false"
icon="el-icon-chat-dot-square"
@click="view(row)"
>
回复
</el-link>
<el-popconfirm
class="ele-action"
title="确定要删除此消息吗?"
@confirm="remove(row)"
>
<template v-slot:reference>
<el-link type="danger" :underline="false" icon="el-icon-delete">
删除
</el-link>
</template>
</el-popconfirm>
</template>
</ele-pro-table>
</template>
<script>
import { pageLetters } from '@/api/user/message';
export default {
data() {
return {
// 表格列配置
columns: [
{
columnKey: 'selection',
type: 'selection',
width: 45,
align: 'center',
fixed: 'left'
},
{
columnKey: 'index',
type: 'index',
width: 45,
align: 'center',
showOverflowTooltip: true,
fixed: 'left'
},
{
prop: 'title',
label: '私信内容',
showOverflowTooltip: true,
minWidth: 110
},
{
prop: 'time',
label: '发送时间',
align: 'center',
showOverflowTooltip: true,
width: 140
},
{
prop: 'status',
label: '状态',
align: 'center',
showOverflowTooltip: true,
width: 80,
slot: 'status'
},
{
columnKey: 'action',
label: '操作',
align: 'center',
showOverflowTooltip: true,
width: 140,
resizable: false,
slot: 'action'
}
],
// 列表选中数据
selection: []
};
},
methods: {
/* 表格数据源 */
datasource({ page, limit, where, order }) {
return pageLetters({ ...where, ...order, page, limit });
},
/* 刷新表格 */
reload(where) {
this.$refs.table.reload({ page: 1, where: where });
},
/* 查看 */
view(row) {
this.$message.info(row.title);
},
/* 删除单个 */
remove(row) {
console.log(row);
this.$message.info('点击了删除');
this.updateUnReadNum();
},
/* 批量删除 */
removeBatch() {
if (!this.selection.length) {
this.$message.error('请至少选择一条数据');
return;
}
this.$message.info('点击了删除');
this.updateUnReadNum();
},
/* 标记已读 */
read() {
if (!this.selection.length) {
this.$message.error('请至少选择一条数据');
return;
}
this.selection.forEach((d) => {
d.status = 1;
});
this.updateUnReadNum();
},
/* 触发更新未读数量事件 */
updateUnReadNum() {
this.$emit('update-data');
}
}
};
</script>
@@ -0,0 +1,158 @@
<template>
<ele-pro-table
ref="table"
:columns="columns"
:datasource="datasource"
:selection.sync="selection"
tool-class="ele-toolbar-actions"
>
<template v-slot:toolbar>
<el-button size="small" type="primary" @click="read">
批量已读
</el-button>
<!--
<el-button size="small" type="danger" @click="removeBatch">
删除通知
</el-button>
-->
</template>
<template v-slot:readCn="{ row }">
<span :class="['ele-text-danger', 'ele-text-info'][row.read]">
{{ row.readCn }}
</span>
</template>
<template v-slot:action="{ row }">
<el-link
type="primary"
:underline="false"
icon="el-icon-view"
@click="view(row)"
>
查看
</el-link>
<!--
<el-popconfirm
class="ele-action"
title="确定要删除此通知吗?"
@confirm="remove(row)"
>
<template v-slot:reference>
<el-link type="danger" :underline="false" icon="el-icon-delete">
删除
</el-link>
</template>
</el-popconfirm>
-->
</template>
</ele-pro-table>
</template>
<script>
// import { pageNotices } from '@/api/user/message';
import { pageNotice, setRead } from '@/api/user/notice';
export default {
data() {
return {
// 表格列配置
columns: [
{
columnKey: 'selection',
type: 'selection',
width: 45,
align: 'center',
fixed: 'left'
},
{
prop: 'content',
label: '通知内容',
showOverflowTooltip: true,
minWidth: 110
},
{
prop: 'c_time',
label: '通知时间',
align: 'center',
showOverflowTooltip: true,
width: 150
},
{
prop: 'readCn',
label: '状态',
align: 'center',
showOverflowTooltip: true,
width: 80,
slot: 'readCn'
},
{
columnKey: 'action',
label: '操作',
align: 'center',
showOverflowTooltip: true,
width: 140,
resizable: false,
slot: 'action'
}
],
// 列表选中数据
selection: []
};
},
methods: {
/* 表格数据源 */
datasource({ page, limit, where, order }) {
return pageNotice({ ...where, ...order, page, limit });
},
/* 刷新表格 */
reload(where) {
this.$refs.table.reload({ page: 1, where: where });
},
/* 查看 */
view(row) {
this.$message.info(row.content);
},
/* 删除单个 */
remove(row) {
console.log(row);
this.$message.info('点击了删除');
this.updateUnReadNum();
},
/* 批量删除 */
removeBatch() {
if (!this.selection.length) {
this.$message.error('请至少选择一条数据');
return;
}
this.$message.info('点击了删除');
this.updateUnReadNum();
},
/* 标记已读 */
read() {
if (!this.selection.length) {
this.$message.error('请至少选择一条数据');
return;
}
let ids = [];
this.selection.forEach((d) => {
ids.push(d.id);
});
setRead({ ids: ids })
.then((result) => {
console.log(result);
this.selection.forEach((d) => {
d.read = 1;
d.readCn = '已读';
});
this.updateUnReadNum();
})
.catch((e) => {
this.$message.error(e.message);
});
},
/* 触发更新未读数量事件 */
updateUnReadNum() {
this.$emit('update-data');
}
}
};
</script>
@@ -0,0 +1,149 @@
<template>
<ele-pro-table
ref="table"
:columns="columns"
:datasource="datasource"
:selection.sync="selection"
tool-class="ele-toolbar-actions"
>
<template v-slot:toolbar>
<el-button size="small" type="primary" @click="read">
批量完成
</el-button>
<el-button size="small" type="danger" @click="removeBatch">
删除待办
</el-button>
</template>
<template v-slot:status="{ row }">
<span :class="['ele-text-danger', 'ele-text-info'][row.status]">
{{ ['未完成', '已完成'][row.status] }}
</span>
</template>
<template v-slot:action="{ row }">
<el-link
type="primary"
:underline="false"
icon="el-icon-finished"
@click="view(row)"
>
完成
</el-link>
<el-popconfirm
class="ele-action"
title="确定要取消此待办吗?"
@confirm="remove(row)"
>
<template v-slot:reference>
<el-link type="danger" :underline="false" icon="el-icon-circle-close">
取消
</el-link>
</template>
</el-popconfirm>
</template>
</ele-pro-table>
</template>
<script>
import { pageTodos } from '@/api/user/message';
export default {
data() {
return {
// 表格列配置
columns: [
{
columnKey: 'selection',
type: 'selection',
width: 45,
align: 'center',
fixed: 'left'
},
{
columnKey: 'index',
type: 'index',
width: 45,
align: 'center',
showOverflowTooltip: true,
fixed: 'left'
},
{
prop: 'title',
label: '待办内容',
showOverflowTooltip: true,
minWidth: 110
},
{
prop: 'time',
label: '结束时间',
align: 'center',
showOverflowTooltip: true,
width: 140
},
{
prop: 'status',
label: '状态',
align: 'center',
showOverflowTooltip: true,
width: 80,
slot: 'status'
},
{
columnKey: 'action',
label: '操作',
align: 'center',
showOverflowTooltip: true,
width: 140,
resizable: false,
slot: 'action'
}
],
// 列表选中数据
selection: []
};
},
methods: {
/* 表格数据源 */
datasource({ page, limit, where, order }) {
return pageTodos({ ...where, ...order, page, limit });
},
/* 刷新表格 */
reload(where) {
this.$refs.table.reload({ page: 1, where: where });
},
/* 查看 */
view(row) {
this.$message.info(row.title);
},
/* 删除单个 */
remove(row) {
console.log(row);
this.$message.info('点击了删除');
this.updateUnReadNum();
},
/* 批量删除 */
removeBatch() {
if (!this.selection.length) {
this.$message.error('请至少选择一条数据');
return;
}
this.$message.info('点击了删除');
this.updateUnReadNum();
},
/* 标记已读 */
read() {
if (!this.selection.length) {
this.$message.error('请至少选择一条数据');
return;
}
this.selection.forEach((d) => {
d.status = 1;
});
this.updateUnReadNum();
},
/* 触发更新未读数量事件 */
updateUnReadNum() {
this.$emit('update-data');
}
}
};
</script>
+179
View File
@@ -0,0 +1,179 @@
<template>
<div :class="['ele-body', { 'demo-message-responsive': styleResponsive }]">
<el-card shadow="never" body-style="padding: 0;">
<div class="ele-cell ele-cell-align-top ele-user-message">
<el-menu
:mode="mode"
:default-active="active"
class="ele-scrollbar-hide"
>
<el-menu-item index="notice">
<router-link to="/user/notice?type=notice">
<el-badge
v-if="unReadNotice"
:value="unReadNotice"
class="ele-badge-static"
/>
<span>系统通知</span>
</router-link>
</el-menu-item>
<!--
<el-menu-item index="letter">
<router-link to="/user/notice?type=letter">
<el-badge
v-if="unReadLetter"
:value="unReadLetter"
class="ele-badge-static"
/>
<span>用户私信</span>
</router-link>
</el-menu-item>
<el-menu-item index="todo">
<router-link to="/user/notice?type=todo">
<el-badge
v-if="unReadTodo"
:value="unReadTodo"
class="ele-badge-static"
/>
<span>代办事项</span>
</router-link>
</el-menu-item>
-->
</el-menu>
<div class="ele-cell-content" style="overflow-x: hidden">
<transition name="slide-right" mode="out-in">
<message-notice
v-if="active === 'notice'"
@update-data="queryUnReadNum"
/>
<message-letter
v-else-if="active === 'letter'"
@update-data="queryUnReadNum"
/>
<message-todo v-else @update-data="queryUnReadNum" />
</transition>
</div>
</div>
</el-card>
</div>
</template>
<script>
import MessageNotice from './components/message-notice.vue';
import MessageLetter from './components/message-letter.vue';
import MessageTodo from './components/message-todo.vue';
// import { getUnReadNum } from '@/api/user/message';
import { unRead } from '@/api/user/notice';
export default {
name: 'UserMessage',
components: { MessageNotice, MessageLetter, MessageTodo },
data() {
return {
// 导航选中
active: null,
// 通知未读数量
unReadNotice: 0,
// 私信未读数量
unReadLetter: 0,
// 代办未读数量
unReadTodo: 0
};
},
computed: {
// 小屏幕水平导航
mode() {
return this.styleResponsive && this.$store.state.theme.screenWidth < 768
? 'horizontal'
: 'vertical';
},
// 是否开启响应式布局
styleResponsive() {
return this.$store.state.theme.styleResponsive;
}
},
watch: {
$route: {
handler(route) {
if (route.path === '/user/notice') {
this.active = route?.query?.type || 'notice';
}
},
immediate: true
}
},
created() {
this.queryUnReadNum();
},
methods: {
/* 查询未读数量 */
queryUnReadNum() {
unRead({})
.then((result) => {
this.unReadNotice = result.noticeCount;
// this.unReadLetter = result.letter;
// this.unReadTodo = result.todo;
})
.catch((e) => {
this.$message.error(e.message);
});
}
}
};
</script>
<style lang="scss" scoped>
.ele-user-message {
:deep(.el-menu) {
width: 151px;
flex-shrink: 0;
}
.ele-cell-content {
padding: 15px;
box-sizing: border-box;
overflow: auto;
}
.ele-badge-static {
margin-right: 10px;
}
}
@media screen and (min-width: 768px) {
.demo-message-responsive .ele-user-message :deep(.el-menu) {
.el-menu-item {
text-align: right;
}
.el-menu-item:first-child {
margin-top: 15px;
}
}
}
@media screen and (max-width: 768px) {
.demo-message-responsive .ele-user-message {
display: block;
:deep(.el-menu) {
width: auto;
text-align: center;
white-space: nowrap;
overflow: auto;
.el-menu-item {
height: 45px;
line-height: 45px;
padding: 0 5px;
display: inline-block;
float: none;
}
}
.ele-badge-static {
margin-left: 3px;
}
}
}
</style>