add 站内通知、私信、websocket服务
This commit is contained in:
parent
ab1d7bee42
commit
f00cf6db39
80
src/api/system/notice/sysNotice.ts
Normal file
80
src/api/system/notice/sysNotice.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import request from '/@/utils/request'
|
||||
// 查询通知私信列表
|
||||
export function listSysNotice(query:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNotice/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function listShowNotice(query:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNotice/listShow',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
// 查询通知私信详细
|
||||
export function getSysNotice(id:number) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNotice/get',
|
||||
method: 'get',
|
||||
params: {
|
||||
id: id.toString()
|
||||
}
|
||||
})
|
||||
}
|
||||
// 新增通知私信
|
||||
export function addSysNotice(data:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNotice/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 修改通知私信
|
||||
export function updateSysNotice(data:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNotice/edit',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 删除通知私信
|
||||
export function delSysNotice(ids:number[]) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNotice/delete',
|
||||
method: 'delete',
|
||||
data:{
|
||||
ids:ids
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export function getIndexData(){
|
||||
return request(
|
||||
{
|
||||
url:'/api/v1/system/sysNotice/getIndexData',
|
||||
method:'get'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
//获取维度通知私信公告信息
|
||||
export function unReadCount(){
|
||||
return request({
|
||||
url:'/api/v1/system/sysNotice/unReadCount',
|
||||
method:'get'
|
||||
})
|
||||
}
|
||||
|
||||
//获取私信待选择的用户
|
||||
export function getUserList(query:string){
|
||||
return request({
|
||||
url:'/api/v1/system/sysNotice/userList',
|
||||
method:'get',
|
||||
params:{userNickname:query}
|
||||
})
|
||||
}
|
52
src/api/system/notice/sysNoticeRead.ts
Normal file
52
src/api/system/notice/sysNoticeRead.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import request from '/@/utils/request'
|
||||
// 查询已读记录列表
|
||||
export function listSysNoticeRead(query:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNoticeRead/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
// 查询已读记录详细
|
||||
export function getSysNoticeRead(id:number) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNoticeRead/get',
|
||||
method: 'get',
|
||||
params: {
|
||||
id: id.toString()
|
||||
}
|
||||
})
|
||||
}
|
||||
// 新增已读记录
|
||||
export function addSysNoticeRead(data:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNoticeRead/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
export function readNotice(data:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNoticeRead/readNotice',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 修改已读记录
|
||||
export function updateSysNoticeRead(data:object) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNoticeRead/edit',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 删除已读记录
|
||||
export function delSysNoticeRead(ids:number[]) {
|
||||
return request({
|
||||
url: '/api/v1/system/sysNoticeRead/delete',
|
||||
method: 'delete',
|
||||
data:{
|
||||
ids:ids
|
||||
}
|
||||
})
|
||||
}
|
@ -36,7 +36,7 @@
|
||||
<i class="fa-trash fa" title="清除缓存"></i>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon">
|
||||
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
|
||||
<el-popover ref="newPopoverRef" placement="bottom" trigger="click" transition="el-zoom-in-top" :width="500" :persistent="false">
|
||||
<template #reference>
|
||||
<el-badge :is-dot="true">
|
||||
<el-icon :title="$t('message.user.title4')">
|
||||
@ -45,7 +45,7 @@
|
||||
</el-badge>
|
||||
</template>
|
||||
<template #default>
|
||||
<UserNews />
|
||||
<UserNews @hideNews="hideNews" />
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
@ -80,9 +80,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, getCurrentInstance, computed, reactive, toRefs, onMounted, defineComponent } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import {ref, getCurrentInstance, computed, reactive, toRefs, onMounted, defineComponent, watch} from 'vue';
|
||||
import { useRoute,useRouter } from 'vue-router';
|
||||
import {ElMessageBox, ElMessage, ElNotification} from 'element-plus';
|
||||
import screenfull from 'screenfull';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@ -94,6 +94,8 @@ import UserNews from '/@/layout/navBars/breadcrumb/userNews.vue';
|
||||
import Search from '/@/layout/navBars/breadcrumb/search.vue';
|
||||
import {logout} from "/@/api/login";
|
||||
import {removeCache} from "/@/api/system/cache";
|
||||
import {noticeStore} from "/@/stores/noticeStore";
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
name: 'layoutBreadcrumbUser',
|
||||
@ -102,11 +104,13 @@ export default defineComponent({
|
||||
const { t } = useI18n();
|
||||
const { proxy } = <any>getCurrentInstance();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const stores = useUserInfo();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { userInfos } = storeToRefs(stores);
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const searchRef = ref();
|
||||
const newPopoverRef=ref();
|
||||
const state = reactive({
|
||||
isScreenfull: false,
|
||||
disabledI18n: 'zh-cn',
|
||||
@ -194,6 +198,10 @@ export default defineComponent({
|
||||
const onSearchClick = () => {
|
||||
searchRef.value.openSearch();
|
||||
};
|
||||
const hideNews=()=>{
|
||||
debugger
|
||||
newPopoverRef.value.hide()
|
||||
}
|
||||
// 组件大小改变
|
||||
const onComponentSizeChange = (size: string) => {
|
||||
Local.remove('themeConfig');
|
||||
@ -253,6 +261,32 @@ export default defineComponent({
|
||||
initComponentSize();
|
||||
}
|
||||
});
|
||||
const noticeStoreAct = noticeStore()
|
||||
const getMessages = computed(() => {
|
||||
return noticeStoreAct.message;
|
||||
});
|
||||
watch(getMessages,(nv,ov)=>{
|
||||
if (!nv || !nv.id) {
|
||||
return;
|
||||
}
|
||||
showNotice(nv)
|
||||
},{ immediate: true, deep: true })
|
||||
const showNotice = (data:any) => {
|
||||
const eln = ElNotification({
|
||||
title: '新消息',
|
||||
message: `您有一条新消息:【${data.title}】,请点击查看详情。`,
|
||||
type: 'warning',
|
||||
duration:3600000,
|
||||
onClick(){
|
||||
if(route.fullPath=="/system/sysNotice/show?type="+data.type){
|
||||
router.go(0)
|
||||
}else{
|
||||
router.push("/system/sysNotice/show?type="+data.type)
|
||||
}
|
||||
eln.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
return {
|
||||
userInfos,
|
||||
onLayoutSetingClick,
|
||||
@ -262,6 +296,7 @@ export default defineComponent({
|
||||
onComponentSizeChange,
|
||||
onLanguageChange,
|
||||
removeCacheClick,
|
||||
hideNews,
|
||||
searchRef,
|
||||
layoutUserFlexNum,
|
||||
...toRefs(state),
|
||||
|
@ -1,115 +1,308 @@
|
||||
<template>
|
||||
<div class="layout-navbars-breadcrumb-user-news">
|
||||
<div class="head-box">
|
||||
<div class="head-box-title">{{ $t('message.user.newTitle') }}</div>
|
||||
<div class="head-box-btn" v-if="newsList.length > 0" @click="onAllReadClick">{{ $t('message.user.newBtn') }}</div>
|
||||
</div>
|
||||
<div class="content-box">
|
||||
<template v-if="newsList.length > 0">
|
||||
<div class="content-box-item" v-for="(v, k) in newsList" :key="k">
|
||||
<div>{{ v.label }}</div>
|
||||
<div class="content-box-msg">
|
||||
{{ v.value }}
|
||||
</div>
|
||||
<div class="content-box-time">{{ v.time }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty :description="$t('message.user.newDesc')" v-else></el-empty>
|
||||
</div>
|
||||
<div class="foot-box" @click="onGoToGiteeClick" v-if="newsList.length > 0">{{ $t('message.user.newGo') }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-tabs v-model="tabsActive" @tab-change="handleTabChange">
|
||||
<el-tab-pane :name="1">
|
||||
<template v-slot:label>
|
||||
<span style="font-size: 18px;font-weight: 500;">通知</span>
|
||||
<el-badge type="warning" :value="count.notify" style="margin-left: 10px"/>
|
||||
</template>
|
||||
|
||||
<div class="layout-navbars-breadcrumb-user-news">
|
||||
<div class="content-box">
|
||||
<template v-if="noticeList.length > 0">
|
||||
<div class="content-box-item" v-for="(v, k) in noticeList" :key="k">
|
||||
<div @click="handleRead(v)">
|
||||
<div>{{ v.title }}</div>
|
||||
<div class="content-box-msg">
|
||||
{{ v.content }}
|
||||
</div>
|
||||
<span class="content-box-time">{{ v.createdAt }}</span>
|
||||
<span style="float: right">
|
||||
<el-tag type="success" effect="plain" v-if="v.isRead==true">已读</el-tag>
|
||||
<el-tag type="danger" effect="plain" v-else>未读</el-tag>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty :description="$t('message.user.newDesc')" v-else></el-empty>
|
||||
</div>
|
||||
<div class="foot-box" v-if="noticeList.length > 0">
|
||||
<!-- <el-button @click="hendleClear('公告')" size="80">清空</el-button>-->
|
||||
<!-- <el-button @click="hendleAllread('公告')" size="80">全部已读</el-button>-->
|
||||
<el-button @click="hendleShowMore()" size="80">查看更多</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-tab-pane>
|
||||
|
||||
|
||||
<el-tab-pane :name="2">
|
||||
<template v-slot:label>
|
||||
<span style="font-size: 18px;font-weight: 500;"> 私信</span>
|
||||
<el-badge type="danger" :value="count.notice" style="margin-left: 10px"/>
|
||||
</template>
|
||||
|
||||
<div class="layout-navbars-breadcrumb-user-news">
|
||||
<div class="content-box">
|
||||
<template v-if="noticeList.length > 0">
|
||||
<div class="content-box-item" v-for="(v, k) in noticeList" :key="k">
|
||||
<div @click="handleRead(v)">
|
||||
<div>{{ v.title }}</div>
|
||||
<div class="content-box-msg" v-html="v.content">
|
||||
|
||||
</div>
|
||||
<span class="content-box-time">{{ v.createdAt }}</span>
|
||||
<span style="float: right">
|
||||
|
||||
<el-tag type="success" effect="plain" v-if="v.isRead==true">已读</el-tag>
|
||||
<el-tag type="danger" effect="plain" v-else>未读</el-tag>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty :description="$t('message.user.newDesc')" v-else></el-empty>
|
||||
</div>
|
||||
<div class="foot-box" v-if="noticeList.length > 0">
|
||||
<!-- <el-button @click="hendleClear('通知')" size="80">清空</el-button>-->
|
||||
<!-- <el-button @click="hendleAllread('通知')" size="80">全部已读</el-button>-->
|
||||
<el-button @click="hendleShowMore()" size="80">查看更多</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!--查看更多弹出框-->
|
||||
<el-dialog v-model="isShowDialog" width="1200px" :close-on-click-modal="false" :destroy-on-close="true">
|
||||
<el-tabs v-model="activeInfo" type="border-card">
|
||||
<el-tab-pane :name="1">
|
||||
<template v-slot:label>
|
||||
<span style="font-weight: 600;font-size: 18px;color: #6c6c6c ">通知</span>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="Config" :name="2">
|
||||
<template v-slot:label>
|
||||
<span style="font-weight: 600;font-size: 18px;color: #6c6c6c ">私信</span>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent } from 'vue';
|
||||
|
||||
import {reactive, toRefs, defineComponent, computed, watch} from 'vue';
|
||||
import {
|
||||
listShowNotice,
|
||||
unReadCount,
|
||||
} from "/@/api/system/notice/sysNotice";
|
||||
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
||||
import {readNotice} from "/@/api/system/notice/sysNoticeRead";
|
||||
import {SysNoticeInfoData} from "/@/views/system/sysNotice/list/component/model";
|
||||
import {useRouter} from "vue-router";
|
||||
export default defineComponent({
|
||||
name: 'layoutBreadcrumbUserNews',
|
||||
setup() {
|
||||
const state = reactive({
|
||||
newsList: [
|
||||
{
|
||||
label: '关于版本发布的通知',
|
||||
value: 'vue-next-admin,基于 vue3 + CompositionAPI + typescript + vite + element plus,正式发布时间:2021年02月28日!',
|
||||
time: '2020-12-08',
|
||||
},
|
||||
{
|
||||
label: '关于学习交流的通知',
|
||||
value: 'QQ群号码 665452019,欢迎小伙伴入群学习交流探讨!',
|
||||
time: '2020-12-08',
|
||||
},
|
||||
],
|
||||
});
|
||||
// 全部已读点击
|
||||
const onAllReadClick = () => {
|
||||
state.newsList = [];
|
||||
};
|
||||
// 前往通知中心点击
|
||||
const onGoToGiteeClick = () => {
|
||||
window.open('https://gitee.com/tiger1103/gfast');
|
||||
};
|
||||
return {
|
||||
onAllReadClick,
|
||||
onGoToGiteeClick,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
name: 'layoutBreadcrumbUserNews',
|
||||
created() {
|
||||
this.getUnReadCount()
|
||||
this.getData(1)
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const state = reactive({
|
||||
type1Num: 0,
|
||||
type2Num: 0,
|
||||
// notifyList: [],
|
||||
noticeList: [],
|
||||
count: {
|
||||
notify: 0,
|
||||
notice: 0
|
||||
},
|
||||
tabsActive: 1,
|
||||
activeInfo: 1,
|
||||
isShowDialog: false,
|
||||
barName: "通知"
|
||||
});
|
||||
|
||||
/** 改变tab*/
|
||||
const handleTabChange = (tabName: number) => {
|
||||
if (tabName === 1) {
|
||||
state.barName = "通知"
|
||||
} else {
|
||||
state.barName = "公告"
|
||||
}
|
||||
getData(tabName)
|
||||
};
|
||||
//获取维度信息
|
||||
const getUnReadCount = () => {
|
||||
unReadCount().then((res: any) => {
|
||||
if (res.data != null) {
|
||||
state.count.notice = res.data.noticeCount || 0
|
||||
state.count.notify = res.data.notifyCount || 0
|
||||
}
|
||||
})
|
||||
}
|
||||
//获取数据
|
||||
const getData = (barName: number | undefined) => {
|
||||
/* let notifyParam = {
|
||||
pageNum: 1,
|
||||
pageSize: 5,
|
||||
type: barName,
|
||||
}
|
||||
listSysNotice(notifyParam).then((res: any) => {
|
||||
console.log("listSysNotice",res)
|
||||
state.notifyList = res.data.list || []
|
||||
})*/
|
||||
let noticeParam = {
|
||||
pageNum: 1,
|
||||
pageSize: 5,
|
||||
type: barName,
|
||||
}
|
||||
listShowNotice(noticeParam).then((res: any) => {
|
||||
state.noticeList = res.data.list || []
|
||||
})
|
||||
};
|
||||
const readAllItem = () => {
|
||||
|
||||
}
|
||||
// 全部已读点击
|
||||
const onAllReadClick = () => {
|
||||
state.noticeList = [];
|
||||
};
|
||||
// 前往通知中心点击
|
||||
const onGoToGiteeClick = () => {
|
||||
/*window.open('https://gitee.com/tiger1103/gfast');*/
|
||||
state.isShowDialog = true
|
||||
};
|
||||
const hendleClear = (type: string) => {
|
||||
ElMessageBox.confirm("是否清除全部" + type + "!", '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {/*
|
||||
delSysNotice(id).then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
})*/
|
||||
let typeIndex = (type == "通知") ? 1 : 2
|
||||
let query = {
|
||||
type: typeIndex,
|
||||
}
|
||||
/* clearNews(query).then((res: any) => {
|
||||
console.log(res)
|
||||
ElMessage.success('清空成功');
|
||||
})*/
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
|
||||
};
|
||||
const hendleAllread = (type: string) => {
|
||||
|
||||
ElMessageBox.confirm("是否将全部" + type + "标记为已读!", '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {/*
|
||||
delSysNotice(id).then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
})*/
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
};
|
||||
const hendleShowMore = () => {
|
||||
// console.log(emit)
|
||||
router.push("/system/sysNotice/show")
|
||||
};
|
||||
|
||||
|
||||
const handleRead = (item: SysNoticeInfoData) => {
|
||||
// console.log("handleRead", item)
|
||||
let query = {
|
||||
noticeId: item.id
|
||||
}
|
||||
readNotice(query).then(() => {
|
||||
// console.log(res)
|
||||
getData(item.type)
|
||||
ElMessage.success("已读");
|
||||
})
|
||||
}
|
||||
return {
|
||||
getData,
|
||||
readAllItem,
|
||||
onAllReadClick,
|
||||
onGoToGiteeClick,
|
||||
hendleClear,
|
||||
hendleAllread,
|
||||
hendleShowMore,
|
||||
getUnReadCount,
|
||||
handleRead,
|
||||
...toRefs(state),
|
||||
handleTabChange
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-navbars-breadcrumb-user-news {
|
||||
.head-box {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
box-sizing: border-box;
|
||||
color: var(--el-text-color-primary);
|
||||
justify-content: space-between;
|
||||
height: 35px;
|
||||
align-items: center;
|
||||
.head-box-btn {
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-box {
|
||||
font-size: 13px;
|
||||
.content-box-item {
|
||||
padding-top: 12px;
|
||||
&:last-of-type {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.content-box-msg {
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.content-box-time {
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.foot-box {
|
||||
height: 35px;
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
:deep(.el-empty__description p) {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.content-box {
|
||||
font-size: 13px;
|
||||
|
||||
.content-box-item {
|
||||
padding-top: 12px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
|
||||
&:last-of-type {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.content-box-msg {
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 5px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.content-box-time {
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.foot-box {
|
||||
height: 35px;
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/* border-top: 1px solid var(--el-border-color-lighter);*/
|
||||
margin-top: 10px;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-empty__description p) {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-tabs__active-bar) {
|
||||
padding-left: 100px;
|
||||
padding-right: 100px;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__item) {
|
||||
width: 200px;
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
|
11
src/main.ts
11
src/main.ts
@ -13,6 +13,7 @@ import '/@/theme/index.scss';
|
||||
import mitt from 'mitt';
|
||||
import VueGridLayout from 'vue-grid-layout';
|
||||
import {getUpFileUrl, handleTree, parseTime, selectDictLabel} from '/@/utils/gfast';
|
||||
import Websocket from '/@/utils/websocket';
|
||||
import {useDict} from '/@/api/system/dict/data';
|
||||
import {getItems, setItems, getOptionValue, isEmpty} from '/@/api/items'
|
||||
// 分页组件
|
||||
@ -27,6 +28,16 @@ import VueUeditorWrap from 'vue-ueditor-wrap';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// 全局websocket
|
||||
const onMessageList: Array<Function> = [];
|
||||
app.provide('onMessageList', onMessageList);
|
||||
const onMessage = (event: any) => {
|
||||
onMessageList.forEach((f) => {
|
||||
f.call(null, event);
|
||||
});
|
||||
};
|
||||
Websocket(onMessage);
|
||||
|
||||
directive(app);
|
||||
other.elSvg(app);
|
||||
|
||||
|
@ -95,3 +95,13 @@ export interface ThemeConfigStates {
|
||||
export interface bigUploadStates {
|
||||
panelShow : boolean
|
||||
}
|
||||
|
||||
export interface NoticeMessage{
|
||||
id:number;
|
||||
title:string;
|
||||
tag:number;
|
||||
type:number;
|
||||
}
|
||||
export interface NoticeStates{
|
||||
message:NoticeMessage;
|
||||
}
|
||||
|
19
src/stores/noticeStore.ts
Normal file
19
src/stores/noticeStore.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import {NoticeMessage, NoticeStates} from "/@/stores/interface";
|
||||
|
||||
export const noticeStore = defineStore({
|
||||
id: 'noticeStore',
|
||||
state: (): NoticeStates => <NoticeStates>({
|
||||
message: {}
|
||||
}),
|
||||
getters: {
|
||||
getMessages(): NoticeMessage {
|
||||
return this.message;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setMessages(messages:NoticeMessage) {
|
||||
this.message = messages;
|
||||
},
|
||||
},
|
||||
});
|
169
src/utils/websocket.ts
Normal file
169
src/utils/websocket.ts
Normal file
@ -0,0 +1,169 @@
|
||||
import {getToken} from "/@/utils/gfast"
|
||||
import {noticeStore} from "/@/stores/noticeStore";
|
||||
const noticeStoreAct = noticeStore()
|
||||
const webSocketURL = import.meta.env.VITE_WEBSOCKET_URL
|
||||
let socket: WebSocket;
|
||||
let isActive: boolean;
|
||||
|
||||
export function getSocket(): WebSocket {
|
||||
if (socket === undefined) {
|
||||
location.reload();
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
export function getActive(): boolean {
|
||||
return isActive;
|
||||
}
|
||||
|
||||
export function sendMsg(event: string, data = null, isRetry = true) {
|
||||
if (socket === undefined || !isActive) {
|
||||
if (!isRetry) {
|
||||
console.log('socket连接异常,发送失败!');
|
||||
return;
|
||||
}
|
||||
console.log('socket连接异常,等待重试..');
|
||||
setTimeout(function () {
|
||||
sendMsg(event, data);
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: event,
|
||||
data: data,
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
// @ts-ignore
|
||||
console.log('ws发送消息失败,等待重试,err:' + err.message);
|
||||
if (!isRetry) {
|
||||
return;
|
||||
}
|
||||
setTimeout(function () {
|
||||
sendMsg(event, data);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
export function addOnMessage(onMessageList: any, func: Function) {
|
||||
let exist = false;
|
||||
for (let i = 0; i < onMessageList.length; i++) {
|
||||
if (onMessageList[i].name == func.name) {
|
||||
onMessageList[i] = func;
|
||||
exist = true;
|
||||
}
|
||||
}
|
||||
if (!exist) {
|
||||
onMessageList.push(func);
|
||||
}
|
||||
}
|
||||
|
||||
export default (onMessage: Function) => {
|
||||
const heartCheck = {
|
||||
timeout: 10000,
|
||||
timeoutObj: setTimeout(() => {}),
|
||||
serverTimeoutObj: setInterval(() => {}),
|
||||
reset: function () {
|
||||
clearTimeout(this.timeoutObj);
|
||||
clearTimeout(this.serverTimeoutObj);
|
||||
return this;
|
||||
},
|
||||
start: function () {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const self = this;
|
||||
clearTimeout(this.timeoutObj);
|
||||
clearTimeout(this.serverTimeoutObj);
|
||||
this.timeoutObj = setTimeout(function () {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: 'ping',
|
||||
})
|
||||
);
|
||||
self.serverTimeoutObj = setTimeout(function () {
|
||||
console.log('关闭服务');
|
||||
socket.close();
|
||||
}, self.timeout);
|
||||
}, this.timeout);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
let lockReconnect = false;
|
||||
let timer: ReturnType<typeof setTimeout>;
|
||||
const createSocket = () => {
|
||||
console.log('createSocket...');
|
||||
try {
|
||||
if (getToken() === '') {
|
||||
throw new Error('用户未登录,稍后重试...');
|
||||
}
|
||||
socket = new WebSocket(webSocketURL+'/api/v1/websocket?token='+encodeURIComponent(getToken()));
|
||||
init();
|
||||
} catch (e) {
|
||||
console.log('createSocket err:' + e);
|
||||
reconnect();
|
||||
}
|
||||
if (lockReconnect) {
|
||||
lockReconnect = false;
|
||||
}
|
||||
};
|
||||
const reconnect = () => {
|
||||
console.log('lockReconnect:' + lockReconnect);
|
||||
if (lockReconnect) return;
|
||||
lockReconnect = true;
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
createSocket();
|
||||
}, 30000);
|
||||
};
|
||||
|
||||
const init = () => {
|
||||
socket.onopen = function (_) {
|
||||
console.log('WebSocket:已连接');
|
||||
heartCheck.reset().start();
|
||||
isActive = true;
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
isActive = true;
|
||||
// console.log('WebSocket:收到一条消息', event.data);
|
||||
let isHeart = false;
|
||||
const message = JSON.parse(event.data);
|
||||
if (message.event === 'ping') {
|
||||
isHeart = true;
|
||||
}
|
||||
// 通知私信
|
||||
if (message.event === 'notice') {
|
||||
noticeStoreAct.setMessages(message.data)
|
||||
return;
|
||||
}
|
||||
if (onMessage && !isHeart) {
|
||||
onMessage.call(null, event);
|
||||
}
|
||||
heartCheck.reset().start();
|
||||
};
|
||||
|
||||
socket.onerror = function (_) {
|
||||
console.log('WebSocket:发生错误');
|
||||
reconnect();
|
||||
isActive = false;
|
||||
};
|
||||
|
||||
socket.onclose = function (_) {
|
||||
console.log('WebSocket:已关闭');
|
||||
heartCheck.reset();
|
||||
reconnect();
|
||||
isActive = false;
|
||||
};
|
||||
|
||||
window.onbeforeunload = function () {
|
||||
socket.close();
|
||||
isActive = false;
|
||||
};
|
||||
};
|
||||
|
||||
createSocket();
|
||||
};
|
||||
|
300
src/views/system/sysNotice/list/component/NoticeMessageEdit.vue
Normal file
300
src/views/system/sysNotice/list/component/NoticeMessageEdit.vue
Normal file
@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<div class="system-sysNotice-edit">
|
||||
<!-- 添加或修改私信对话框 -->
|
||||
<el-dialog v-model="isShowDialog" width="800px" :close-on-click-modal="false" :destroy-on-close="true">
|
||||
<template #header>
|
||||
<div v-drag="['.system-sysNotice-edit .el-dialog', '.system-sysNotice-edit .el-dialog__header']">
|
||||
<span style="font-weight: 500;font-size: 18px;color: rgb(31 34 37);"> {{
|
||||
(!formData.id || formData.id == 0 ? '发送' : '修改') + title
|
||||
}} </span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="80px">
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="formData.title" placeholder="请输入标题"/>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="类型" prop="type">
|
||||
<el-select v-model="formData.type" placeholder="请选择类型">
|
||||
<el-option label="请选择字典生成" value=""/>
|
||||
</el-select>
|
||||
</el-form-item>-->
|
||||
|
||||
<el-form-item v-if="formData.type==2" label="接收用户">
|
||||
<el-select
|
||||
style="width: 100%;"
|
||||
v-model="formData.receiver"
|
||||
multiple
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="可输入要指定的用户名称搜索"
|
||||
:remote-method="remoteUserMethod"
|
||||
:loading="loading"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userListOptions"
|
||||
:key="item.id"
|
||||
:label="item.userNickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="标签" prop="tag">
|
||||
<el-select v-model="formData.tag" placeholder="请选择标签">
|
||||
<el-option
|
||||
v-for="dict in tagOptions"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="内容">
|
||||
<gf-ueditor editorId="ueSysNoticeContent" v-model="formData.content"
|
||||
@setEditContent="setContentEditContent"></gf-ueditor>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<!-- <el-radio-group v-model="formData.status">
|
||||
<el-radio>请选择字典生成</el-radio>
|
||||
</el-radio-group>-->
|
||||
<el-switch
|
||||
v-model="formData.status"
|
||||
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-text="正常"
|
||||
inactive-text="停用"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input show-word-limit maxlength="200" v-model="formData.remark" type="textarea" placeholder="请输入备注"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number type="num" v-model="formData.sort"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确 定</el-button>
|
||||
<el-button @click="onCancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {reactive, onMounted, toRefs, defineComponent, ref, unref, getCurrentInstance} from 'vue';
|
||||
import { ElMessage} from 'element-plus';
|
||||
import {
|
||||
getSysNotice,
|
||||
addSysNotice,
|
||||
updateSysNotice, getUserList,
|
||||
} from "/@/api/system/notice/sysNotice";
|
||||
import GfUeditor from "/@/components/ueditor/index.vue"
|
||||
import {
|
||||
SysNoticeInfoData,
|
||||
SysNoticeEditState
|
||||
} from "/@/views/system/sysNotice/list/component/model"
|
||||
|
||||
export default defineComponent({
|
||||
name: "NoticeMessageEdit",
|
||||
components: {
|
||||
GfUeditor,
|
||||
},
|
||||
props: {
|
||||
tagOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
},
|
||||
setup(props, {emit}) {
|
||||
const {proxy} = <any>getCurrentInstance()
|
||||
const formRef = ref<HTMLElement | null>(null);
|
||||
const menuRef = ref();
|
||||
const state = reactive<SysNoticeEditState>({
|
||||
loading: false,
|
||||
isShowDialog: false,
|
||||
title: "",
|
||||
userListOptions: [],
|
||||
formData: {
|
||||
id: undefined,
|
||||
receiver: undefined,
|
||||
title: undefined,
|
||||
type: undefined,
|
||||
tag: undefined,
|
||||
content: undefined,
|
||||
remark: undefined,
|
||||
sort: undefined,
|
||||
status: 0,
|
||||
createdBy: undefined,
|
||||
updatedBy: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined,
|
||||
deletedAt: undefined,
|
||||
},
|
||||
|
||||
// 表单校验
|
||||
rules: {
|
||||
id: [
|
||||
{required: true, message: "ID不能为空", trigger: "blur"}
|
||||
],
|
||||
title: [
|
||||
{required: true, message: "标题不能为空", trigger: "blur"}
|
||||
],
|
||||
type: [
|
||||
{required: true, message: "类型不能为空", trigger: "blur"}
|
||||
],
|
||||
content: [
|
||||
{required: true, message: "内容不能为空", trigger: "blur"}
|
||||
],
|
||||
status: [
|
||||
{required: true, message: "状态不能为空", trigger: "blur"}
|
||||
],
|
||||
}
|
||||
});
|
||||
onMounted(() => {
|
||||
remoteUserMethod("");
|
||||
});
|
||||
// 打开弹窗
|
||||
const openDialog = (row?: SysNoticeInfoData) => {
|
||||
resetForm();
|
||||
if (row) {
|
||||
getSysNotice(row.id!).then((res: any) => {
|
||||
const data = res.data;
|
||||
data.type = parseInt(data.type)
|
||||
if(data.type===2&&data.receiverUser){
|
||||
const userListOptions = [...state.userListOptions,...data.receiverUser]
|
||||
let uniqueSet = new Set(userListOptions.map(item => item.id));
|
||||
state.userListOptions = userListOptions.filter((value, index, self) => {
|
||||
return uniqueSet.has(value.id) && uniqueSet.delete(value.id)
|
||||
});
|
||||
}
|
||||
data.tag = '' + data.tag
|
||||
data.status = parseInt(data.status)
|
||||
state.formData = data;
|
||||
})
|
||||
}
|
||||
state.isShowDialog = true;
|
||||
};
|
||||
|
||||
const remoteUserMethod = (query: string) => {
|
||||
//console.log("remoteMethod", query)
|
||||
state.userListOptions = []
|
||||
getUserList(query).then((res: any) => {
|
||||
/*console.log(res)*/
|
||||
// let list:object[]
|
||||
//list=res.data
|
||||
state.userListOptions = res.data.userList
|
||||
})
|
||||
/* if (query) {
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
options.value = list.value.filter((item) => {
|
||||
return item.label.toLowerCase().includes(query.toLowerCase())
|
||||
})
|
||||
}, 200)
|
||||
} else {
|
||||
options.value = []
|
||||
}*/
|
||||
}
|
||||
//设置类型
|
||||
const setType = (type: number) => {
|
||||
state.formData.type = type
|
||||
if (type == 1) {
|
||||
state.title = "通知"
|
||||
} else if (type == 2) {
|
||||
state.title = "私信"
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const closeDialog = () => {
|
||||
state.isShowDialog = false;
|
||||
};
|
||||
// 取消
|
||||
const onCancel = () => {
|
||||
closeDialog();
|
||||
};
|
||||
// 提交
|
||||
const onSubmit = () => {
|
||||
const formWrap = unref(formRef) as any;
|
||||
if (!formWrap) return;
|
||||
formWrap.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
state.loading = true;
|
||||
if (!state.formData.id || state.formData.id === 0) {
|
||||
//添加
|
||||
addSysNotice(state.formData).then(() => {
|
||||
ElMessage.success('添加成功');
|
||||
closeDialog(); // 关闭弹窗
|
||||
emit('sysNoticeList')
|
||||
}).finally(() => {
|
||||
state.loading = false;
|
||||
})
|
||||
} else {
|
||||
//修改
|
||||
updateSysNotice(state.formData).then(() => {
|
||||
ElMessage.success('修改成功');
|
||||
closeDialog(); // 关闭弹窗
|
||||
emit('sysNoticeList')
|
||||
}).finally(() => {
|
||||
state.loading = false;
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const resetForm = () => {
|
||||
state.formData = {
|
||||
receiver: undefined,
|
||||
type: undefined,
|
||||
id: undefined,
|
||||
title: undefined,
|
||||
tag: undefined,
|
||||
content: undefined,
|
||||
remark: undefined,
|
||||
sort: 0,
|
||||
status: 1,
|
||||
createdBy: undefined,
|
||||
updatedBy: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined,
|
||||
deletedAt: undefined
|
||||
}
|
||||
};
|
||||
//富文本编辑器内容
|
||||
const setContentEditContent = (data: string) => {
|
||||
state.formData.content = data
|
||||
}
|
||||
return {
|
||||
proxy,
|
||||
openDialog,
|
||||
setType,
|
||||
closeDialog,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
remoteUserMethod,
|
||||
menuRef,
|
||||
formRef,
|
||||
//富文本编辑器内容
|
||||
setContentEditContent,
|
||||
...toRefs(state),
|
||||
};
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.kv-label {
|
||||
margin-bottom: 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mini-btn i.el-icon {
|
||||
margin: unset;
|
||||
}
|
||||
|
||||
.kv-row {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
61
src/views/system/sysNotice/list/component/model.ts
Normal file
61
src/views/system/sysNotice/list/component/model.ts
Normal file
@ -0,0 +1,61 @@
|
||||
export interface SysNoticeTableColumns {
|
||||
id:number; // ID
|
||||
title:string; // 标题
|
||||
type:number; // 类型
|
||||
tag:number; // 标签
|
||||
content:string; // 内容
|
||||
remark:string; // 备注
|
||||
sort:number; // 排序
|
||||
status:number; // 状态
|
||||
createdBy:string; // 发送人
|
||||
createdAt:string; // 创建时间
|
||||
}
|
||||
|
||||
|
||||
export interface SysNoticeInfoData {
|
||||
id:number|undefined; // ID
|
||||
receiver:number[]|undefined;//用户id
|
||||
title:string|undefined; // 标题
|
||||
type:number|undefined; // 类型
|
||||
tag:number|undefined; // 标签
|
||||
content:string|undefined; // 内容
|
||||
remark:string|undefined; // 备注
|
||||
sort:number|undefined; // 排序
|
||||
status:number; // 状态
|
||||
createdBy:number|undefined; // 发送人
|
||||
updatedBy:number|undefined; // 修改人
|
||||
createdAt:string|undefined; // 创建时间
|
||||
updatedAt:string|undefined; // 更新时间
|
||||
deletedAt:string|undefined; // 删除时间
|
||||
}
|
||||
|
||||
|
||||
export interface SysNoticeTableDataState {
|
||||
ids:any[];
|
||||
tableData: {
|
||||
data: Array<SysNoticeTableColumns>;
|
||||
total: number;
|
||||
loading: boolean;
|
||||
param: {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
id: number|undefined;
|
||||
title: string|undefined;
|
||||
type: number|undefined;
|
||||
tag: number|undefined;
|
||||
status: number|undefined;
|
||||
createdAt: string|undefined;
|
||||
dateRange: string[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export interface SysNoticeEditState{
|
||||
loading:boolean;
|
||||
isShowDialog: boolean;
|
||||
userListOptions:object[];
|
||||
title:string;
|
||||
formData:SysNoticeInfoData;
|
||||
rules: object;
|
||||
}
|
375
src/views/system/sysNotice/list/index.vue
Normal file
375
src/views/system/sysNotice/list/index.vue
Normal file
@ -0,0 +1,375 @@
|
||||
<template>
|
||||
<div class="system-sysNotice-container">
|
||||
<el-card shadow="hover">
|
||||
<div class="system-sysNotice-search mb15">
|
||||
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="5" class="colBlock">
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input
|
||||
v-model="tableData.param.title"
|
||||
placeholder="请输入标题"
|
||||
clearable
|
||||
@keyup.enter.native="sysNoticeList"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5" class="colBlock">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="tableData.param.status" placeholder="请选择状态" clearable>
|
||||
<el-option label="正常" :value="1"/>
|
||||
<el-option label="停用" :value="0"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5" class="colBlock">
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select v-model="tableData.param.type" placeholder="请选择类型" clearable>
|
||||
<el-option label="通知" value="1"/>
|
||||
<el-option label="公告" value="2"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5" class="colBlock">
|
||||
<el-form-item label="标签" prop="tag">
|
||||
<el-select v-model="tableData.param.tag" placeholder="请选择标签" clearable>
|
||||
<el-option
|
||||
v-for="dict in notice_tag"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4" class="colBlock">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="sysNoticeList">
|
||||
<el-icon>
|
||||
<ele-Search/>
|
||||
</el-icon>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery(queryRef)">
|
||||
<el-icon>
|
||||
<ele-Refresh/>
|
||||
</el-icon>
|
||||
重置
|
||||
</el-button>
|
||||
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleAdd(1)"
|
||||
v-auth="'api/v1/system/sysNotice/noticeAdd'"
|
||||
>
|
||||
<el-icon>
|
||||
<ele-Plus/>
|
||||
</el-icon>
|
||||
发通知
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleAdd(2)"
|
||||
v-auth="'api/v1/system/sysNotice/messageAdd'"
|
||||
>
|
||||
<el-icon>
|
||||
<ele-Plus/>
|
||||
</el-icon>
|
||||
发私信
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
:disabled="single"
|
||||
@click="handleUpdate(null)"
|
||||
v-auth="'api/v1/system/sysNotice/edit'"
|
||||
>
|
||||
<el-icon>
|
||||
<ele-Edit/>
|
||||
</el-icon>
|
||||
修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete(null)"
|
||||
v-auth="'api/v1/system/sysNotice/delete'"
|
||||
>
|
||||
<el-icon>
|
||||
<ele-Delete/>
|
||||
</el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="tableData.data" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center"/>
|
||||
<el-table-column label="ID" align="center" prop="id" min-width="50px"/>
|
||||
<el-table-column label="标题" align="left" prop="title" min-width="150px"/>
|
||||
<el-table-column label="类型" align="center" prop="type" min-width="150px">
|
||||
<template #default="scope">
|
||||
<el-tag type="warning" v-if="scope.row.type=='1'">通知</el-tag>
|
||||
<el-tag type="danger" v-if="scope.row.type=='2'">私信</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标签" align="center" prop="tag" :formatter="tagFormat" min-width="80px">
|
||||
<template #default="scope">
|
||||
<el-tag type="info" v-if="scope.row.tag==0">无标签</el-tag>
|
||||
<el-tag type="success" v-if="scope.row.tag==1">提醒</el-tag>
|
||||
<el-tag type="success" v-if="scope.row.tag==2">一般</el-tag>
|
||||
<el-tag type="warning" v-if="scope.row.tag==3">次要</el-tag>
|
||||
<el-tag type="danger" v-if="scope.row.tag==4">重要</el-tag>
|
||||
<el-tag type="danger" v-if="scope.row.tag==5">紧急</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="内容" align="center" prop="content" min-width="150px"/>-->
|
||||
<el-table-column label="备注" align="center" prop="remark" min-width="150px"/>
|
||||
<el-table-column label="阅读次数" align="center" prop="clickNumber" min-width="150px"/>
|
||||
<el-table-column label="排序" align="center" prop="sort" min-width="50px"/>
|
||||
<el-table-column label="状态" align="center" prop="status" min-width="80px">
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.status==1">正常</el-tag>
|
||||
<el-tag type="danger" v-if="scope.row.status==0">停用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="发送人" align="center" prop="createdBy" min-width="150px"/>-->
|
||||
<el-table-column label="发送时间" align="center" prop="createdAt" min-width="100px">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding" min-width="160px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-auth="'api/v1/system/sysNotice/edit'"
|
||||
>
|
||||
<el-icon>
|
||||
<ele-EditPen/>
|
||||
</el-icon>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleDelete(scope.row)"
|
||||
v-auth="'api/v1/system/sysNotice/delete'"
|
||||
>
|
||||
<el-icon>
|
||||
<ele-DeleteFilled/>
|
||||
</el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="tableData.total>0"
|
||||
:total="tableData.total"
|
||||
v-model:page="tableData.param.pageNum"
|
||||
v-model:limit="tableData.param.pageSize"
|
||||
@pagination="sysNoticeList"
|
||||
/>
|
||||
</el-card>
|
||||
<NoticeMessageEdit
|
||||
ref="editRef"
|
||||
:tagOptions="notice_tag"
|
||||
@sysNoticeList="sysNoticeList"
|
||||
></NoticeMessageEdit>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
|
||||
import {toRefs, reactive, onMounted, ref, defineComponent, computed, getCurrentInstance, toRaw} from 'vue';
|
||||
import {ElMessageBox, ElMessage, FormInstance} from 'element-plus';
|
||||
import {
|
||||
listSysNotice,
|
||||
delSysNotice,
|
||||
} from "/src/api/system/notice/sysNotice";
|
||||
import {
|
||||
SysNoticeTableColumns,
|
||||
SysNoticeInfoData,
|
||||
SysNoticeTableDataState,
|
||||
} from "/@/views/system/sysNotice/list/component/model"
|
||||
import NoticeMessageEdit from "/@/views/system/sysNotice/list/component/NoticeMessageEdit.vue"
|
||||
export default defineComponent({
|
||||
name: "apiV1SystemSysNoticeList",
|
||||
components: {
|
||||
NoticeMessageEdit
|
||||
},
|
||||
setup() {
|
||||
const {proxy} = <any>getCurrentInstance()
|
||||
const loading = ref(false)
|
||||
const queryRef = ref()
|
||||
const editRef = ref();
|
||||
|
||||
// 是否显示所有搜索选项
|
||||
const showAll = ref(false)
|
||||
// 非单个禁用
|
||||
const single = ref(true)
|
||||
// 非多个禁用
|
||||
const multiple = ref(true)
|
||||
const word = computed(() => {
|
||||
if (showAll.value === false) {
|
||||
//对文字进行处理
|
||||
return "展开搜索";
|
||||
} else {
|
||||
return "收起搜索";
|
||||
}
|
||||
})
|
||||
// 字典选项数据
|
||||
const {
|
||||
notice_tag,
|
||||
} = proxy.useDict(
|
||||
'notice_tag',
|
||||
)
|
||||
const state = reactive<SysNoticeTableDataState>({
|
||||
ids: [],
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
id: undefined,
|
||||
title: undefined,
|
||||
type: undefined,
|
||||
tag: undefined,
|
||||
status: undefined,
|
||||
createdAt: undefined,
|
||||
dateRange: []
|
||||
},
|
||||
},
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initTableData();
|
||||
});
|
||||
// 初始化表格数据
|
||||
const initTableData = () => {
|
||||
sysNoticeList()
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
sysNoticeList()
|
||||
};
|
||||
// 获取列表数据
|
||||
const sysNoticeList = () => {
|
||||
loading.value = true
|
||||
listSysNotice(state.tableData.param).then((res: any) => {
|
||||
let list = res.data.list ?? [];
|
||||
list.map((item: any) => {
|
||||
item.createdBy = item.createdUser?.userNickname
|
||||
})
|
||||
state.tableData.data = list;
|
||||
state.tableData.total = res.data.total;
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
const toggleSearch = () => {
|
||||
showAll.value = !showAll.value;
|
||||
}
|
||||
// 标签字典翻译
|
||||
const tagFormat = (row: SysNoticeTableColumns) => {
|
||||
return proxy.selectDictLabel(notice_tag.value, row.tag);
|
||||
}
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: Array<SysNoticeInfoData>) => {
|
||||
state.ids = selection.map(item => item.id)
|
||||
single.value = selection.length != 1
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
|
||||
const handleAdd = (type: number) => {
|
||||
editRef.value.openDialog()
|
||||
editRef.value.setType(type)
|
||||
}
|
||||
const handleUpdate = (row: SysNoticeTableColumns) => {
|
||||
if (!row) {
|
||||
row = state.tableData.data.find((item: SysNoticeTableColumns) => {
|
||||
return item.id === state.ids[0]
|
||||
}) as SysNoticeTableColumns
|
||||
}
|
||||
editRef.value.openDialog(toRaw(row));
|
||||
};
|
||||
const handleDelete = (row: SysNoticeTableColumns) => {
|
||||
let msg = '你确定要删除所选数据?';
|
||||
let id: number[] = [];
|
||||
if (row) {
|
||||
msg = `此操作将永久删除数据,是否继续?`
|
||||
id = [row.id]
|
||||
} else {
|
||||
id = state.ids
|
||||
}
|
||||
if (id.length === 0) {
|
||||
ElMessage.error('请选择要删除的数据。');
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(msg, '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
delSysNotice(id).then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
sysNoticeList();
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
}
|
||||
return {
|
||||
proxy,
|
||||
editRef,
|
||||
showAll,
|
||||
loading,
|
||||
single,
|
||||
multiple,
|
||||
word,
|
||||
queryRef,
|
||||
resetQuery,
|
||||
sysNoticeList,
|
||||
toggleSearch,
|
||||
tagFormat,
|
||||
notice_tag,
|
||||
handleSelectionChange,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
handleDelete,
|
||||
...toRefs(state),
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.colBlock {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.colNone {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin: 3px;
|
||||
}
|
||||
</style>
|
259
src/views/system/sysNotice/show/index.vue
Normal file
259
src/views/system/sysNotice/show/index.vue
Normal file
@ -0,0 +1,259 @@
|
||||
<template>
|
||||
<div class="system-sysNotice-container">
|
||||
<el-card shadow="hover">
|
||||
<el-tabs
|
||||
v-model="tableData.param.type"
|
||||
type="card"
|
||||
@tab-click="handleTabsClick"
|
||||
>
|
||||
<el-tab-pane label="通知" :name="1">
|
||||
<el-table :data="tableData.data" border style="width: 100%">
|
||||
<el-table-column>
|
||||
<template #default="scope">
|
||||
<el-row @click="handleRead(scope.row)">
|
||||
<el-col :span="0.8">
|
||||
<el-icon class="el_icon">
|
||||
<ele-Bell class="icon"/>
|
||||
</el-icon>
|
||||
</el-col>
|
||||
<el-col :span="23">
|
||||
<span class="title" style="">{{ scope.row.title }}</span>
|
||||
<span style="margin-left: 6px">
|
||||
<el-tag type="success" effect="plain" v-if="scope.row.isRead==true">已读</el-tag>
|
||||
<el-tag type="danger" effect="plain" v-else>未读</el-tag>
|
||||
</span>
|
||||
<span style="float: right">
|
||||
<el-tag type="info" v-if="scope.row.tag==0">无标签</el-tag>
|
||||
<el-tag type="success" v-if="scope.row.tag==1">提醒</el-tag>
|
||||
<el-tag type="success" v-if="scope.row.tag==2">一般</el-tag>
|
||||
<el-tag type="warning" v-if="scope.row.tag==3">次要</el-tag>
|
||||
<el-tag type="danger" v-if="scope.row.tag==4">重要</el-tag>
|
||||
<el-tag type="danger" v-if="scope.row.tag==5">紧急</el-tag>
|
||||
</span>
|
||||
<br>
|
||||
<span>{{ scope.row.createdAt }}</span>
|
||||
<br>
|
||||
<span v-html="scope.row.content"></span>
|
||||
<br>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="私信" :name="2">
|
||||
<el-table :data="tableData.data" border style="width: 100%">
|
||||
<el-table-column>
|
||||
<template #default="scope">
|
||||
<el-row @click="handleRead(scope.row)">
|
||||
<el-col :span="0.8">
|
||||
<el-icon class="el_icon">
|
||||
<ele-ChatDotRound class="icon"/>
|
||||
</el-icon>
|
||||
</el-col>
|
||||
<el-col :span="23">
|
||||
<span class="title" style="">{{ scope.row.title }}</span>
|
||||
<span style="margin-left: 6px">
|
||||
|
||||
<el-tag type="success" effect="plain" v-if="scope.row.isRead==true">已读</el-tag>
|
||||
<el-tag type="danger" effect="plain" v-else>未读</el-tag>
|
||||
</span>
|
||||
<span style="float: right">
|
||||
<el-tag type="info" v-if="scope.row.tag==0">无标签</el-tag>
|
||||
<el-tag type="success" v-if="scope.row.tag==1">提醒</el-tag>
|
||||
<el-tag type="success" v-if="scope.row.tag==2">一般</el-tag>
|
||||
<el-tag type="warning" v-if="scope.row.tag==3">次要</el-tag>
|
||||
<el-tag type="danger" v-if="scope.row.tag==4">重要</el-tag>
|
||||
<el-tag type="danger" v-if="scope.row.tag==5">紧急</el-tag>
|
||||
</span>
|
||||
<br>
|
||||
<span>{{ scope.row.createdAt }}</span>
|
||||
<span v-html="scope.row.content"></span>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<pagination
|
||||
v-show="tableData.total>0"
|
||||
:total="tableData.total"
|
||||
v-model:page="tableData.param.pageNum"
|
||||
v-model:limit="tableData.param.pageSize"
|
||||
@pagination="sysNoticeList"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {useRoute} from "vue-router";
|
||||
import {toRefs, reactive, onMounted, ref, defineComponent, getCurrentInstance} from 'vue';
|
||||
import {ElMessageBox, ElMessage, FormInstance} from 'element-plus';
|
||||
import {
|
||||
delSysNotice, listShowNotice,
|
||||
} from "/@/api/system/notice/sysNotice";
|
||||
import {
|
||||
SysNoticeTableColumns,
|
||||
SysNoticeTableDataState,
|
||||
} from "/@/views/system/sysNotice/list/component/model"
|
||||
import {readNotice} from "/@/api/system/notice/sysNoticeRead";
|
||||
|
||||
export default defineComponent({
|
||||
name: "",
|
||||
components: {},
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const {proxy} = <any>getCurrentInstance()
|
||||
const loading = ref(false)
|
||||
const state = reactive<SysNoticeTableDataState>({
|
||||
ids: [],
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
param: {
|
||||
pageNum: 1,
|
||||
pageSize: 3,
|
||||
id: undefined,
|
||||
title: undefined,
|
||||
type: 1,
|
||||
tag: undefined,
|
||||
status: undefined,
|
||||
createdAt: undefined,
|
||||
dateRange: []
|
||||
},
|
||||
},
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
if (route.query.type){
|
||||
state.tableData.param.type = parseInt(route.query.type as string)
|
||||
}
|
||||
initTableData();
|
||||
});
|
||||
// 初始化表格数据
|
||||
const initTableData = () => {
|
||||
sysNoticeList()
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
sysNoticeList()
|
||||
};
|
||||
// 获取列表数据
|
||||
const sysNoticeList = () => {
|
||||
loading.value = true
|
||||
listShowNotice(state.tableData.param).then((res: any) => {
|
||||
let list = res.data.list ?? [];
|
||||
list.map((item: any) => {
|
||||
item.createdBy = item.createdUser?.userNickname
|
||||
})
|
||||
state.tableData.data = list;
|
||||
state.tableData.total = res.data.total;
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
const handleTabsClick = (e: any) => {
|
||||
//console.log(e.props.name)
|
||||
state.tableData.param.type = e.props.name
|
||||
sysNoticeList()
|
||||
}
|
||||
|
||||
|
||||
const handleDelete = (row: SysNoticeTableColumns) => {
|
||||
let msg = '你确定要删除所选数据?';
|
||||
let id: number[] = [];
|
||||
if (row) {
|
||||
msg = `此操作将永久删除数据,是否继续?`
|
||||
id = [row.id]
|
||||
} else {
|
||||
id = state.ids
|
||||
}
|
||||
if (id.length === 0) {
|
||||
ElMessage.error('请选择要删除的数据。');
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(msg, '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
delSysNotice(id).then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
sysNoticeList();
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line no-undef
|
||||
const handleRead = (item: any) => {
|
||||
// console.log("handleRead", item)
|
||||
let query = {
|
||||
noticeId: item.id
|
||||
}
|
||||
readNotice(query).then(() => {
|
||||
sysNoticeList()
|
||||
ElMessage.success("已读");
|
||||
})
|
||||
}
|
||||
return {
|
||||
proxy,
|
||||
loading,
|
||||
resetQuery,
|
||||
sysNoticeList,
|
||||
handleTabsClick,
|
||||
handleDelete,
|
||||
handleRead,
|
||||
...toRefs(state),
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.el_icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 10px;
|
||||
background-color: #2d8cf0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #0a0a0a
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
color: #F8F8FF
|
||||
}
|
||||
|
||||
.colBlock {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.colNone {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
|
||||
:deep(.el-table__header-wrapper) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user