Commit 3214856b by 郑艺斌

fix: 新增消息中心模块

parent d8e6cfad
import request from "@/utils/httpRequest";
export default class Message {
// 消息列表
getMessagePage(data) {
return request({
url: `/message/pageList`,
method: "post",
data,
});
}
// 阅读
seeRead(id) {
return request({
url: `/message/read`,
method: "post",
params: { id: id },
});
}
// 全部已读
setReadAll(data) {
return request({
url: `/message/readAll`,
method: "post",
data,
});
}
// 获取消息类型未读数量
getMessageTypeCount(id) {
return request({
url: `/message/typeCount`,
method: "post",
params: { userId: id },
});
}
// 消息总未读数量
getMessageCount(id) {
return request({
url: `/message/count`,
method: "post",
params: { userId: id },
});
}
}
...@@ -2,104 +2,180 @@ ...@@ -2,104 +2,180 @@
* 全站路由配置 * 全站路由配置
* 代码中路由统一使用path属性跳转 * 代码中路由统一使用path属性跳转
*/ */
import Vue from 'vue' import Vue from "vue";
import Router from 'vue-router' import Router from "vue-router";
import {isURL} from '@/utils/validate' import { isURL } from "@/utils/validate";
import {clearLoginInfo} from '@/utils' import { clearLoginInfo } from "@/utils";
Vue.use(Router) Vue.use(Router);
const routerPush = Router.prototype.push const routerPush = Router.prototype.push;
Router.prototype.push = function push (location) { Router.prototype.push = function push(location) {
return routerPush.call(this, location).catch(error => error) return routerPush.call(this, location).catch((error) => error);
} };
// 开发环境不使用懒加载 // 开发环境不使用懒加载
const _import = require('./import-' + process.env.NODE_ENV) const _import = require("./import-" + process.env.NODE_ENV);
// 全局路由 // 全局路由
const globalRoutes = [ const globalRoutes = [
{path: '/login', component: _import('modules/sys/login/login'), name: 'login', meta: {title: '登录'}}, {
{path: '/casLogin', component: _import('common/CasLogin'), name: 'casLogin', meta: { title: 'CAS登录' }} path: "/login",
] component: _import("modules/sys/login/login"),
name: "login",
meta: { title: "登录" },
},
{
path: "/casLogin",
component: _import("common/CasLogin"),
name: "casLogin",
meta: { title: "CAS登录" },
},
];
// 主入口路由 // 主入口路由
const mainRoutes = { const mainRoutes = {
path: '/', path: "/",
component: _import('main'), component: _import("main"),
name: 'main', name: "main",
redirect: {name: 'home'}, redirect: { name: "home" },
meta: {title: '整体布局'}, meta: { title: "整体布局" },
children: [ children: [
{path: '/redirect/:path(.*)', component: _import('modules/redirect/index')}, {
{path: '/home', redirect: '/sys/dashboard/analysis/index', name: 'home'}, path: "/redirect/:path(.*)",
{path: '/flowable/task/TaskForm', component: _import('modules/flowable/task/TaskForm'), name: 'task-form', meta: {title: '流程表单'}}, component: _import("modules/redirect/index"),
{path: '/flowable/task/TaskFormEdit', component: _import('modules/flowable/task/TaskFormEdit'), name: 'task-form-edit', meta: {title: '流程表单'}}, },
{path: '/flowable/task/TaskFormDetail', component: _import('modules/flowable/task/TaskFormDetail'), name: 'task-form-detail', meta: {title: '流程表单详情'}}, { path: "/home", redirect: "/sys/dashboard/analysis/index", name: "home" },
{path: '/form/generateList', component: _import('modules/form/GenerateList'), name: 'form-preview-list', meta: {title: '列表'}}, {
{path: '/echarts/GenerateChart', component: _import('modules/echarts/GenerateChart'), name: 'echarts-generate', meta: {title: '预览图表'}}, path: "/flowable/task/TaskForm",
{path: '/ureport/designer', component: null, name: 'ureport-designer', meta: {title: '预览报表', type: 'iframe', menuId: 'ureport-designer'}}, component: _import("modules/flowable/task/TaskForm"),
{path: '/ureport/preview', component: null, name: 'ureport-preview', meta: {title: '预览报表', type: 'iframe', menuId: 'ureport-preview'}}, name: "task-form",
{path: '/form/explorer', component: null, name: 'form-explorer', meta: {title: '浏览器', type: 'iframe'}}, meta: { title: "流程表单" },
{path: '/database/datatable/TableForm', component: _import('modules/database/datatable/TableForm'), name: 'table-form', meta: {title: '数据库表详情'}}, },
{path: '/404', component: _import('common/404'), name: '404', meta: {title: '404未找到'}} {
path: "/flowable/task/TaskFormEdit",
] component: _import("modules/flowable/task/TaskFormEdit"),
} name: "task-form-edit",
meta: { title: "流程表单" },
},
{
path: "/flowable/task/TaskFormDetail",
component: _import("modules/flowable/task/TaskFormDetail"),
name: "task-form-detail",
meta: { title: "流程表单详情" },
},
{
path: "/form/generateList",
component: _import("modules/form/GenerateList"),
name: "form-preview-list",
meta: { title: "列表" },
},
{
path: "/message",
component: _import("modules/message/index"),
name: "message",
meta: { title: "消息中心" },
},
{
path: "/echarts/GenerateChart",
component: _import("modules/echarts/GenerateChart"),
name: "echarts-generate",
meta: { title: "预览图表" },
},
{
path: "/ureport/designer",
component: null,
name: "ureport-designer",
meta: { title: "预览报表", type: "iframe", menuId: "ureport-designer" },
},
{
path: "/ureport/preview",
component: null,
name: "ureport-preview",
meta: { title: "预览报表", type: "iframe", menuId: "ureport-preview" },
},
{
path: "/form/explorer",
component: null,
name: "form-explorer",
meta: { title: "浏览器", type: "iframe" },
},
{
path: "/database/datatable/TableForm",
component: _import("modules/database/datatable/TableForm"),
name: "table-form",
meta: { title: "数据库表详情" },
},
{
path: "/404",
component: _import("common/404"),
name: "404",
meta: { title: "404未找到" },
},
],
};
const router = new Router({ const router = new Router({
mode: 'hash', mode: "hash",
scrollBehavior: () => ({y: 0}), scrollBehavior: () => ({ y: 0 }),
isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由 isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由
routes: globalRoutes.concat(mainRoutes) routes: globalRoutes.concat(mainRoutes),
}) });
// 添加动态(菜单)路由 // 添加动态(菜单)路由
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
let token = Vue.cookie.get('token') let token = Vue.cookie.get("token");
if (!token || !/\S/.test(token)) { // token为空,跳转到login登录 if (!token || !/\S/.test(token)) {
clearLoginInfo() // token为空,跳转到login登录
if (process.env.VUE_APP_SSO_LOGIN === 'true') { // 如果是单点登录 clearLoginInfo();
if (to.name === 'casLogin') { // 单点登录跳转页面获取token if (process.env.VUE_APP_SSO_LOGIN === "true") {
next() // 如果是单点登录
if (to.name === "casLogin") {
// 单点登录跳转页面获取token
next();
} else { } else {
window.location.href = `${process.env.VUE_APP_CAS_SERVER}/login?service=${process.env.VUE_APP_CLIENT_LOGIN}` window.location.href = `${process.env.VUE_APP_CAS_SERVER}/login?service=${process.env.VUE_APP_CLIENT_LOGIN}`;
} }
} else { } else {
if (fnCurrentRouteType(to, globalRoutes) === 'global') { if (fnCurrentRouteType(to, globalRoutes) === "global") {
next() next();
} else { } else {
next({name: 'login'}) next({ name: "login" });
} }
} }
} else if (router.options.isAddDynamicMenuRoutes) { // 如果已经包含权限 } else if (router.options.isAddDynamicMenuRoutes) {
next() // 如果已经包含权限
} else { // 请求权限 next();
let routerList = localStorage.getItem('routerList') } else {
// 请求权限
let routerList = localStorage.getItem("routerList");
if (routerList) { if (routerList) {
router.options.isAddDynamicMenuRoutes = true router.options.isAddDynamicMenuRoutes = true;
fnAddDynamicMenuRoutes(JSON.parse(routerList), []) fnAddDynamicMenuRoutes(JSON.parse(routerList), []);
next({...to, replace: true}) next({ ...to, replace: true });
} else { } else {
clearLoginInfo() clearLoginInfo();
next() next();
console.log(`请求菜单列表和权限失败,跳转至登录页!!`, 'color:blue') console.log(`请求菜单列表和权限失败,跳转至登录页!!`, "color:blue");
} }
} }
}) });
/** /**
* 判断当前路由类型, global: 全局路由, main: 主入口路由 * 判断当前路由类型, global: 全局路由, main: 主入口路由
* @param {*} route 当前路由 * @param {*} route 当前路由
*/ */
function fnCurrentRouteType (route, globalRoutes = []) { function fnCurrentRouteType(route, globalRoutes = []) {
let temp = [] let temp = [];
for (let i = 0; i < globalRoutes.length; i++) { for (let i = 0; i < globalRoutes.length; i++) {
if (route.path === globalRoutes[i].path) { if (route.path === globalRoutes[i].path) {
return 'global' return "global";
} else if (globalRoutes[i].children && globalRoutes[i].children.length >= 1) { } else if (
temp = temp.concat(globalRoutes[i].children) globalRoutes[i].children &&
globalRoutes[i].children.length >= 1
) {
temp = temp.concat(globalRoutes[i].children);
} }
} }
return temp.length >= 1 ? fnCurrentRouteType(route, temp) : 'main' return temp.length >= 1 ? fnCurrentRouteType(route, temp) : "main";
} }
/** /**
...@@ -108,65 +184,75 @@ function fnCurrentRouteType (route, globalRoutes = []) { ...@@ -108,65 +184,75 @@ function fnCurrentRouteType (route, globalRoutes = []) {
* @param {*} routes 递归创建的动态(菜单)路由 * @param {*} routes 递归创建的动态(菜单)路由
*/ */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function fnAddDynamicMenuRoutes (menuList = [], routes = []) { function fnAddDynamicMenuRoutes(menuList = [], routes = []) {
let temp = [] let temp = [];
for (let i = 0; i < menuList.length; i++) { for (let i = 0; i < menuList.length; i++) {
if (menuList[i].children && menuList[i].children.length >= 1) { if (menuList[i].children && menuList[i].children.length >= 1) {
temp = temp.concat(menuList[i].children) temp = temp.concat(menuList[i].children);
} }
if (menuList[i].href && /\S/.test(menuList[i].href)) { if (menuList[i].href && /\S/.test(menuList[i].href)) {
menuList[i].href = menuList[i].href.replace(/[/]$/, '') menuList[i].href = menuList[i].href.replace(/[/]$/, "");
const route = { const route = {
path: menuList[i].href.split('?')[0], path: menuList[i].href.split("?")[0],
component: null, component: null,
name: menuList[i].href.replace(/^\//g, '').replace(/[/]/g, '-').replace(/[?]/g, '-').replace(/&/g, '-').replace(/=/g, '-'), name: menuList[i].href
.replace(/^\//g, "")
.replace(/[/]/g, "-")
.replace(/[?]/g, "-")
.replace(/&/g, "-")
.replace(/=/g, "-"),
meta: { meta: {
parentIds: menuList[i].parentIds, parentIds: menuList[i].parentIds,
menuId: menuList[i].id, menuId: menuList[i].id,
title: menuList[i].name, title: menuList[i].name,
isDynamic: true, isDynamic: true,
type: menuList[i].target, type: menuList[i].target,
affix: menuList[i].affix === '1', affix: menuList[i].affix === "1",
iframeUrl: '' iframeUrl: "",
} },
} };
// url以http[s]://开头, 通过iframe展示 // url以http[s]://开头, 通过iframe展示
if (menuList[i].target === 'iframe') { if (menuList[i].target === "iframe") {
route.path = '/' + route.meta.menuId route.path = "/" + route.meta.menuId;
if (isURL(menuList[i].href)) { if (isURL(menuList[i].href)) {
route['meta']['iframeUrl'] = menuList[i].href route["meta"]["iframeUrl"] = menuList[i].href;
} else { } else {
route['meta']['iframeUrl'] = `${process.env.VUE_APP_SERVER_URL}${menuList[i].href}` route["meta"][
"iframeUrl"
] = `${process.env.VUE_APP_SERVER_URL}${menuList[i].href}`;
} }
} else { } else {
try { try {
if (menuList[i].href) { if (menuList[i].href) {
route['component'] = _import(`modules${menuList[i].href.split('?')[0]}`) || null route["component"] =
_import(`modules${menuList[i].href.split("?")[0]}`) || null;
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e);
} }
} }
let exist = routes.filter(r => let exist =
r.path === route.path routes.filter((r) => r.path === route.path).length === 0 &&
).length === 0 && mainRoutes.children.filter(c => mainRoutes.children.filter((c) => c.path === route.path).length === 0;
c.path === route.path if (exist) {
).length === 0 // 如果路由不存在则添加
if (exist) { // 如果路由不存在则添加 routes.push(route);
routes.push(route)
} }
} }
} }
if (temp.length >= 1) { if (temp.length >= 1) {
fnAddDynamicMenuRoutes(temp, routes) fnAddDynamicMenuRoutes(temp, routes);
} else { } else {
mainRoutes.name = 'main-dynamic' mainRoutes.name = "main-dynamic";
mainRoutes.children = routes mainRoutes.children = routes;
router.addRoute(mainRoutes) router.addRoute(mainRoutes);
router.addRoute({path: '*', redirect: {name: '404'}}) router.addRoute({ path: "*", redirect: { name: "404" } });
localStorage.setItem('dynamicMenuRoutes', JSON.stringify(mainRoutes.children || [])) localStorage.setItem(
"dynamicMenuRoutes",
JSON.stringify(mainRoutes.children || [])
);
} }
} }
export default router export default router;
export default { export default {
namespaced: true, namespaced: true,
state: { state: {
id: '', id: "",
name: '', name: "",
loginName: '', loginName: "",
no: '', no: "",
office: { office: {
id: '', id: "",
name: '' name: "",
}, },
company: { company: {
id: '', id: "",
name: '' name: "",
}, },
photo: '' photo: "",
messageCount: {},
}, },
mutations: { mutations: {
updateId (state, id) { updateId(state, id) {
state.id = id state.id = id;
}, },
updateName (state, name) { updateName(state, name) {
state.name = name state.name = name;
}, },
updateLoginName (state, loginName) { updateLoginName(state, loginName) {
state.loginName = loginName state.loginName = loginName;
}, },
updatePhoto (state, photo) { updatePhoto(state, photo) {
state.photo = photo state.photo = photo;
}, },
updateUser (state, user) { updateUser(state, user) {
state.id = user.id state.id = user.id;
state.name = user.name state.name = user.name;
state.loginName = user.loginName state.loginName = user.loginName;
state.company = user.companyDTO state.company = user.companyDTO;
state.office = user.officeDTO state.office = user.officeDTO;
state.no = user.no state.no = user.no;
state.photo = user.photo state.photo = user.photo;
localStorage.setItem('user', JSON.stringify(user)) localStorage.setItem("user", JSON.stringify(user));
} },
} updateMessageCount(state, messageCount) {
} state.messageCount = messageCount;
},
},
};
...@@ -60,6 +60,12 @@ ...@@ -60,6 +60,12 @@
</el-menu-item> </el-menu-item>
<el-menu-item class="hide-sm"> <el-menu-item class="hide-sm">
<template slot="title"> <template slot="title">
<div class="noticeButton" @click="goMessage">
<el-badge v-if="messageCount.total != 0" :value="messageCount.total" class="badge">
<i class="fa fa-bell-o"></i>
</el-badge>
<i class="fa fa-bell-o" v-else></i>
</div>
<!-- <notice-icon class="action notice" :tabs="noticeTabs"> <!-- <notice-icon class="action notice" :tabs="noticeTabs">
</notice-icon> --> </notice-icon> -->
</template> </template>
...@@ -96,6 +102,8 @@ import ColorPicker from '@/components/colors/ColorPicker' ...@@ -96,6 +102,8 @@ import ColorPicker from '@/components/colors/ColorPicker'
import NotifyService from '@/api/notify/NotifyService' import NotifyService from '@/api/notify/NotifyService'
import MailBoxService from '@/api/mail/MailBoxService' import MailBoxService from '@/api/mail/MailBoxService'
import LoginService from '@/api/auth/LoginService' import LoginService from '@/api/auth/LoginService'
import Message from '@/api/message/index'
export default { export default {
data () { data () {
return { return {
...@@ -123,7 +131,8 @@ export default { ...@@ -123,7 +131,8 @@ export default {
emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg' emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg'
} }
], ],
breadcrumbs: [] breadcrumbs: [],
user: JSON.parse(localStorage.getItem('user')),
} }
}, },
components: { components: {
...@@ -140,6 +149,11 @@ export default { ...@@ -140,6 +149,11 @@ export default {
this.loginService = new LoginService() this.loginService = new LoginService()
}, },
computed: { computed: {
messageCount: {
get () {
return this.$store.state.user.messageCount
},
},
navbarLayoutType () { navbarLayoutType () {
return this.$store.state.common.navbarLayoutType return this.$store.state.common.navbarLayoutType
}, },
...@@ -228,6 +242,7 @@ export default { ...@@ -228,6 +242,7 @@ export default {
}) })
}, },
mounted () { mounted () {
this.getMessageCount()
if (this.defaultLayout === 'top') { if (this.defaultLayout === 'top') {
this.fixTopMenu() this.fixTopMenu()
} }
...@@ -291,6 +306,9 @@ export default { ...@@ -291,6 +306,9 @@ export default {
} }
}, },
methods: { methods: {
goMessage () {
this.$router.push({ name: "message" })
},
// 获取路由名字 // 获取路由名字
getTitle (menus, id, obj) { getTitle (menus, id, obj) {
menus.forEach((menu) => { menus.forEach((menu) => {
...@@ -349,3 +367,16 @@ export default { ...@@ -349,3 +367,16 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
.noticeButton {
width: 100%;
height: 100%;
padding: 0 12px;
display: flex;
align-items: center;
transition: all 0.3s;
}
</style>
<template>
<div class="jp-common-layout page">
<div class="jp-common-layout-left">
<el-row class="rowTabs">
<div v-for="item in tabs" :class="{ rowItem: true, rowActive: item.value == tabsActive }" :key="item.value"
@click="clickTabsItem(item)">
<div class="name">{{item.label}}</div>
<div class="number">{{item.total}}</div>
</div>
</el-row>
</div>
<div class="jp-common-layout-center jp-flex-main">
<div class="bg-white" style="height: 100%;">
<div style="height: calc(100% - 30px);" v-if="tableData.length">
<div class="list">
<div class="list-item" v-for="item in tableData">
<div class="item-left">
<div class="title" :title="item.title">{{item.title}}</div>
<div class="subTitle" :title="item.subTitle">{{item.subTitle}},</div>
<div class="content" :title="item.content">异常内容:{{item.content}}</div>
<div class="date">{{item.createDate}} - 系统发送</div>
</div>
<div class="item-right">
<div v-if="item.readFlag=='0'" @click="setRead(item)"><i class="el-icon-check"
style="margin-right: 2px;"></i>设置已读</div>
<div v-else style="color: #00AFAB;">已读</div>
</div>
</div>
</div>
<div class="footer">
<el-button type="primary" size="small" icon="el-icon-view" @click="seeReadAll">全部设置为已读</el-button>
<vxe-pager background size="small" :current-page="tablePage.currentPage" :page-size="tablePage.pageSize"
:total="tablePage.total" :page-sizes="[10, 20, 100, 1000, { label: '全量数据', value: 1000000 }]"
:layouts="['PrevPage', 'JumpNumber', 'NextPage', 'FullJump', 'Sizes', 'Total']"
@page-change="currentChangeHandle">
</vxe-pager>
</div>
</div>
<div v-else style="height: 100%;display: flex; align-items: center;justify-content: center;">
<el-empty :image-size="200"></el-empty>
</div>
</div>
</div>
</div>
</template>
<script>
import Message from '@/api/message/index'
export default {
Message: null,
name: '',
data () {
return {
searchForm: {
name: '',
},
tableData: [],
loading: false,
tablePage: {
total: 0,
currentPage: 1,
pageSize: 10,
},
tabsActive: '-1',
user: JSON.parse(localStorage.getItem('user')),
tabs: []
}
},
watch: {},
computed: {
},
methods: {
// 切换点击
clickTabsItem (item) {
this.tabsActive = item.value
this.getMessagePage()
},
// 获取列表
getMessagePage () {
this.loading = true
const can = {
userId: this.user.loginName,
type: this.tabsActive,
current: this.tablePage.currentPage,
size: this.tablePage.pageSize
}
this.Message.getMessagePage(can).then(({ data }) => {
this.tableData = data.records
this.tablePage.total = data.total
this.loading = false
})
},
// 分页
currentChangeHandle ({ currentPage, pageSize }) {
this.tablePage.currentPage = currentPage
this.tablePage.pageSize = pageSize
this.getMessagePage()
},
// 获取tabs列表
getMessageTypeCount () {
this.Message.getMessageTypeCount(this.user.loginName).then(({ data }) => {
this.tabs = data.body
this.$store.commit('user/updateMessageCount', data.body[0])
})
},
// 设置已读
setRead (item) {
this.Message.seeRead(item.id).then(({ data }) => {
this.$set(item, 'readFlag', '1')
})
},
// 设置全部已读
seeReadAll () {
const can = {
type: this.tabsActive,
userId: this.user.loginName,
}
this.Message.setReadAll(can).then(({ data }) => {
this.getMessageTypeCount()
this.getMessagePage()
})
}
},
created () {
this.Message = new Message()
},
mounted () {
this.getMessageTypeCount()
this.getMessagePage()
}
}
</script>
<style lang="less" scoped>
.rowTabs {
padding: 10px 0 10px 0;
.rowItem {
padding: 10px 15px 10px 15px;
background: #fff;
letter-spacing: 2px;
display: flex;
justify-content: space-between;
align-items: center;
}
.rowActive {
color: #fff;
background: #00AFAB;
}
.number {
height: 18px;
line-height: 18px;
text-align: center;
padding: 0 6px;
background-color: #F56C6C;
color: #fff;
border-radius: 10px;
font-size: 12px;
}
}
.list {
height: 100%;
overflow: auto;
.list-item {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
padding: 10px 20px 10px 20px;
.item-left {
width: 80%;
.title {
font-weight: bold;
margin-bottom: 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.subTitle {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.content {
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
word-break: break-all;
}
.date {
margin-top: 10px;
color: #ccc;
font-size: 15px;
}
}
.item-right {
div {
cursor: pointer;
}
}
}
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment