马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
结果图:- <img alt="" src="[img]https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvMDI0OGY4ZGIwN2Y4NGNmZTg5YTA0NmNmNGFlZjFlNDUucG5n[/img]" />
复制代码 message.vue 消息组件 子组件- <template>
- <div class="custom-notification">
- <div class="content">
- <span @click="gotoMessageList(currentMessage.split('=')[1])">{{ currentMessage.split('=')[0] }}</span>
- </div>
- <div class="footer">
- <div class="left-buttons">
- <el-button size="mini" type="text" @click="handleIgnoreAll">忽略全部</el-button>
- </div>
- <div class="pagination">
- <el-pagination
- small
- :key="paginationKey"
- :current-page.sync="currentPage"
- :page-size="1"
- :total="localNoticeList.length"
- layout="prev,slot, next"
- prev-text="<"
- next-text=">"
- @prev-click="currentPage--"
- @next-click="currentPage++">
- <span class="pagination-text">{{ currentPage }} / {{ totalPages }}</span>
- </el-pagination>
- </div>
- <div class="right-button">
- <el-button type="primary" size="mini" @click="handleAccept(currentMessage.split('=')[1])">接受</el-button>
- </div>
- </div>
- </div>
- </template>
- <script>
- export default {
- props: {
- messages: {
- type: Array,
- default: () => []
- },
- total:{
- type:Number,
- default:0
- },
- noticeList:{
- type:Array,
- default:()=>[]
- }
- },
- data() {
- return {
- localNoticeList: [], // 新增本地副本
- currentPage: 1,
- totalPage: 5,
- paginationKey: 0 // 分页组件独立key
- }
- },
- watch: {
- noticeList: {
- deep:true,
- immediate: true,
- handler(newVal) {
- this.localNoticeList = JSON.parse(JSON.stringify(newVal))
- // 当消息更新时自动重置页码
- this.currentPage = Math.min(this.currentPage, newVal.length)
- this.paginationKey++ // 强制分页组件重置
- // this.updateList(this.localNoticeList)
- }
- }
- },
- computed: {
- totalPages() {
- return this.localNoticeList.length || 1
- },
- currentMessage() {
- return this.localNoticeList[this.currentPage - 1]?.messageTitle +'='+this.localNoticeList[this.currentPage - 1]?.messageId || ''
- }
- },
- methods: {
- handleLater() {
- // 稍后提醒逻辑
- this.$emit('later')
- },
- handleIgnoreAll() {
- // 忽略全部 将消息全部设为已读
- this.$emit('ignore-all',this.localNoticeList) // 触发父级关闭事件
- },
- handleAccept(msgId) {
- //接收
- this.$emit('gotoMessageList',msgId)
- },
- gotoMessageList(msgId){
- this.$emit('gotoMessageList',msgId)
- },
- // 通过事件通知父组件
- updateList(newList) {
- this.$emit('update-list', newList)
- }
- }
- }
- </script>
- <style scoped>
- .custom-notification {
- padding: 15px 0px 15px 0px;
- width: 270px;
- }
- .header {
- display: flex;
- align-items: center;
- margin-bottom: 12px;
- }
- .content{
- cursor: pointer;
- font-weight: bold;
- font-size: 13px;
- }
- .header i {
- margin-right: 8px;
- font-size: 16px;
- }
- .footer {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-top: 15px;
- }
- .pagination {
- display: flex;
- align-items: center;
- gap: 8px;
- }
- .pagination i {
- cursor: pointer;
- color: #606266;
- }
- .left-buttons .el-button--text {
- color: #909399;
- }
- .pagination-text{
- text-align: center;
- }
- </style>
复制代码 消息父组件:- <template>
- <div>
- <el-badge :value="total" class="item" style="line-height: 40px; position: relative;">
- <span @click="bellClick">
- <el-icon
- class="el-icon-bell"
- style="font-size: 20px; cursor: pointer; transform: translateY(1.1px)"
- ></el-icon>
- </span>
- </el-badge>
- <!-- 添加或修改我的消息对话框 -->
- <el-dialog title="我的消息" :visible.sync="open" width="500px" append-to-body>
- <el-form ref="form" :model="form" :rules="rules" label-width="80px">
- <el-form-item label="消息标题" prop="messageTitle">
- <el-input disabled v-model="form.messageTitle" placeholder="请输入消息标题" />
- </el-form-item>
- <el-form-item label="消息内容">
- <editor :readOnly="true" v-model="form.messageContent" :min-height="192" />
- </el-form-item>
- </el-form>
- <div slot="footer" class="dialog-footer">
- <el-button @click="cancel">关闭</el-button>
- </div>
- </el-dialog>
- <!-- word预览弹窗 -->
- <el-dialog fullscreen title="word预览" :visible.sync="dialogVisibleWord" append-to-body>
- <vue-office-docx :src="previewWordOffice" @rendered="renderedHandler" @error="errorHandler" />
- </el-dialog>
- <!-- doc预览 -->
- <el-dialog fullscreen title="word预览" :visible.sync="DocSync" append-to-body>
- <VueOfficePdf :src="docSrc" />
- </el-dialog>
- <el-dialog
- :title="msg.messageTitle"
- :visible.sync="messageVisible"
- width="800px"
- append-to-body
- class="my-message__dialog"
- @close="closeMsgModel"
- >
- <div><span class="contentImg" v-html="msg.messageContent"></span>
- </div>
- <div class="my-message__filebox" v-if="fileList.length > 0">
- <div>附件:</div>
- <ul class="my-message__ul">
- <li class="my-message__li" v-for="(item, index) in fileList" :key="index">
- <div class="my-message__li--title">{{ item.name }}</div>
- <div class="my-message__li--opt">
- <el-button
- style="color: green"
- @click.stop="handleShow(index, item)"
- type="text"
- icon="el-icon-view"
- size="mini"
- v-if="getFileExtension(item.name) !== 'zip' && getFileExtension(item.name) !== 'rar'"
- >查看
- </el-button>
- </div>
- </li>
- </ul>
- </div>
- <div slot="footer" class="dialog-footer">
- <el-button @click.stop="closeMsgModel">关闭</el-button>
- </div>
- </el-dialog>
- <!-- <custom-notification></custom-notification>-->
- </div>
- </template>
- <script>
- import Vue from 'vue'
- import { mapGetters } from 'vuex';
- //引入VueOfficeDocx组件
- import VueOfficeDocx from '@vue-office/docx';
- import { listMyUnreadNotice, getSysmessage, listSysmessage } from '@/api/system/sysmessage';
- import { fileDownload, previewDocumentAPI } from '@/api/files/files';
- import XLSX from 'xlsx';
- import events from '@/utils/events';
- import VueOfficePdf from '@vue-office/pdf';
- import Notification from 'element-ui/lib/notification'
- import CustomNotification from '@/components/Bell/component/message'
- export default {
- name: 'index',
- components: { VueOfficePdf, VueOfficeDocx,CustomNotification },
- data() {
- return {
- DocSync:false,
- docSrc:'',
- baseUrl: 'http://' + window.location.host + process.env.VUE_APP_BASE_API,
- dialogVisibleWord: false,
- previewWordOffice: '',
- noticeList: [],
- noticeListNew:[],//
- pollInterval: null,// 轮询定时器
- lastCheckTime: null,// 最后检查时间
- notificationKey:0,
- isFirstCheck: true,// 首次检查标志
- templateVisible: false,
- messageVisible:false,
- showAllBtn: false,
- msg:{},
- form: {},
- rules: {},
- open: false,
- queryParams: {
- id: null,
- status: '0'
- },
- fileList: [],
- total: 0,
- totalNew:0,
- notificationInstance:null //通知示例
- };
- },
- computed: {
- ...mapGetters(['isDot', 'id'])
- },
- created() {
- // on接收 emit发送
- this.clearPolling()
- this.getUnRead(); //查询是否有未读消息通知
- this.getUnReadNew(); //初始化调用获取未读消息
- events.$on('noticePush', this.getUnRead);
- events.$on('noticePushByMsg', this.getUnRead);
- this.$nextTick(() => {
- events.$on('noticeCheckByMsg', this.getUnRead);
- // events.$on('noticeCancel',this.cancel);
- });
- events.$on('noticePush',this.getUnReadNew)
- },
- mounted() {
- },
- beforeDestroy() {
- // this.clearPolling()
- },
- // 关闭定时器
- deactivated(){
- // this.clearPolling()
- },
- methods: {
- // 跳转链接
- clickSpan() {
- // 点击跳转我的消息页
- this.$router.push('/task/myMessage');
- },
- /**word预览组件回调函数 */
- renderedHandler() {
- console.log('渲染完成');
- },
- errorHandler() {
- console.log('渲染失败');
- },
- /** 查询是否存在未读消息 根据当前用户匹配消息表中得ID */
- getUnRead() {
- this.queryParams.id = this.id;
- // 查询未读消息
- listSysmessage(this.queryParams).then((res) => {
- if (res.code == 200) {
- if (res.rows.length > 0) {
- // 这里的total 要请求我的代办的接口 然后加起来
- this.total = res.total;
- this.noticeList = res.rows;
- this.$store.dispatch('app/setIsDot', true);
- } else {
- this.total = null;
- this.noticeList = [];
- this.$store.dispatch('app/setIsDot', false);
- }
- this.$emit('getUnRead', this.noticeList);
- }
- });
- },
- //查询未读消息重构 添加定时任务 每隔20分钟调用一次
- /** 核心查询方法 */
- async getUnReadNew() {
- try {
- const params = {
- id: this.id, // 用户ID
- status:0,
- }
- const { code, rows, total } = await listSysmessage(params)
- if (code === 200) {
- this.handleNewMessages(rows, total)
- this.lastCheckTime = new Date()
- }
- } catch (error) {
- console.error('消息检查失败:', error)
- }
- },
- /** 处理新消息逻辑 */
- handleNewMessages(rows, total) {
- // 数据同步
- this.noticeListNew = rows
- this.totalNew = rows.length//total
- // 仅当有新消息时触发通知
- if (rows.length > 0) {
- if (this.notificationInstance) {
- // 手动创建组件实例
- // 通过 key 变化强制更新组件
- this.notificationKey = Date.now()
- // const NotificationComponent = new Vue({
- // render: h => h(CustomNotification, {
- // props: {
- // noticeList: [...this.noticeListNew],
- // total: this.noticeListNew.length
- // },
- // on: {
- // // 事件监听...
- // }
- // })
- // }).$mount()
- // // 保存组件实例引用
- // this.customNotificationInstance = NotificationComponent.$children[0]
- // this.customNotificationInstance = this.notificationInstance.$children[0]
- //已有弹窗时仅更新数据
- this.customNotificationInstance.$set(
- this.customNotificationInstance,
- 'noticeList',
- [...rows]
- )
- this.customNotificationInstance.$forceUpdate()
- } else {
- this.showNotification()
- }
- }
- this.$emit('update:noticeList', rows)
- window.clearInterval(this.pollInterval)
- this.setupPolling()// 启动轮询 后台发送消息时 自动触发
- },
- /** 判断是否有新消息 */
- hasNewMessages(newRows) {
- return newRows.some(item =>
- !this.noticeListNew.find(old => old.messageId === item.messageId)
- )
- },
- /** 轮询控制 */
- setupPolling() {
- // 5分钟调用一次 60000 * 5 300000
- this.clearPolling() // 清除已有定时器
- this.pollInterval = window.setInterval(() => {
- this.getUnReadNew()
- },300000)
- },
- /** 销毁定时器*/
- clearPolling() {
- if (this.pollInterval) {
- window.clearInterval(this.pollInterval)
- this.pollInterval = null
- }
- },
- bellClick() {
- this.getUnRead();
- },
- cancel() {
- this.open = false;
- this.getUnRead();
- },
- cancelTempDialog() {
- this.templateVisible = false;
- this.getUnRead();
- },
- // 查看更多
- showAll() {
- this.$router.push({ path: '/monitor/myMessage' });
- },
- //查看消息 弹窗显示
- handleRead(data) {
- const messageId = data.messageId;
- getSysmessage(messageId).then((response) => {
- this.form = response.data;
- // this.form.messageTitle = data.messageTitle
- // this.form.messageContent = data.messageContent
- if (this.form.fileJSON && JSON.parse(this.form.fileJSON)) {
- this.fileList = JSON.parse(this.form.fileJSON);
- } else {
- this.fileList = [];
- }
- if (this.form.messageType === '3') {
- this.templateVisible = true;
- } else {
- this.open = true;
- this.title = '查看我的消息';
- }
- });
- },
- toLink(url) {
- if (url) {
- if (url.indexOf('http://') > -1 || url.indexOf('https://') > -1) {
- window.open(url);
- } else {
- this.$router.push({ path: url });
- }
- }
- this.cancelTempDialog();
- },
- handleDownLoad(index, file) {
- const param = {
- code: file.code,
- fileName: file.name
- };
- fileDownload(param)
- .then((response) => {
- if (response) {
- const url = window.URL.createObjectURL(new Blob([response]));
- const link = document.createElement('a');
- link.href = url;
- link.setAttribute('download', file.name); // 设置下载的文件名
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- }
- })
- .catch((error) => {
- });
- },
- // 预览
- async handleShow(index, file) {
- const param = {
- code: file.code,
- fileName: file.name
- };
- const type = file.name.split('.')[file.name.split('.').length - 1];
- await fileDownload(param)
- .then(async (response) => {
- if (type === 'pdf' || type === 'PDF') {
- //pdf预览
- let blob = new Blob([response], {
- type: 'application/pdf'
- });
- let fileURL = window.URL.createObjectURL(blob);
- window.open(fileURL, '_blank'); //这里是直接打开新窗口
- } else if (type === 'txt' || type === 'TXT') {
- //txt预览
- // 将文件流转换为文本
- const text = await response.text();
- // 在新窗口中打开文本内容
- const newWindow = window.open();
- newWindow.document.write(`<pre>${text}</pre>`);
- } else if (type === 'xls' || type === 'xlsx') {
- //excel预览
- // 将文件流转换为ArrayBuffer
- const arrayBuffer = await response.arrayBuffer();
- // 使用xlsx插件解析ArrayBuffer为Workbook对象
- const workbook = XLSX.read(arrayBuffer, { type: 'array' });
- // 获取第一个Sheet
- const sheetName = workbook.SheetNames[0];
- const sheet = workbook.Sheets[sheetName];
- // 将Sheet转换为HTML表格
- const html = XLSX.utils.sheet_to_html(sheet);
- // 添加表格线样式
- const styledHtml = `
- <style>
- table {
- border-collapse: collapse;
- }
- td, th {
- border: 1px solid black;
- padding: 8px;
- }
- </style>
- ${html}
- `;
- // 在新窗口中打开HTML内容
- const newWindow = window.open();
- setTimeout(() => {
- newWindow.document.title = sheetName;
- }, 0);
- newWindow.document.write(styledHtml);
- } else if (type === 'doc' || type === 'docx') {
- if (type === 'doc') {
- // doc预览 需要转码 定时任务
- this.handleMany(file.code,type)
- }else{
- // docx预览
- const fileUrl = window.URL.createObjectURL(response);
- this.previewWordOffice = fileUrl;
- this.dialogVisibleWord = true;
- }
- }
- else if (type === 'png' || type === 'jpg') {
- const fileURL = window.URL.createObjectURL(response);
- this.qrCode = fileURL;
- this.dialogVisible = true;
- console.log(fileURL, 'fileURL的值为-----------');
- this.saveFile = file;
- }
- })
- .catch((error) => {});
- },
- // doc 预览
- handleMany(code,type) {
- previewDocumentAPI({code: code}).then(response => {
- let fileURL
- fileURL = this.baseUrl + response.msg
- if (type === 'doc' || type === 'docx') {
- this.docSrc = fileURL
- this.DocSync = true
- }
- })
- },
- showNotification() {
- // 如果已有通知实例且未关闭,先关闭
- if (this.notificationInstance) {
- this.$notify.closeAll()
- }
- const h = this.$createElement
- const notificationNode = h(CustomNotification, {
- key: this.notificationKey, // 添加唯一key
- props: {
- noticeList: [...this.noticeListNew], // 传递消息列表
- total:this.noticeListNew.length
- },
- on: {
- // 添加事件监听
- 'update-list': this.handleListUpdate,
- 'close-notification': () => this.$notify.closeAll(),
- 'later': () => {
- this.$notify.closeAll()
- },
- 'ignore-all': (localNoticeList) => {
- const msgList = localNoticeList
- msgList & msgList.forEach((item)=>{
- this.readMessageAll(item.messageId)
- })
- this.$notify.closeAll()
- this.getUnRead()
- },
- 'accept': () => {
- console.log('接受处理逻辑')
- },
- close: () => {
- this.notificationInstance = null
- },
- 'gotoMessageList': (msgId) => {
- this.readMessage(msgId)
- this.getUnRead()
- //阅读了那一条 根据ID匹配然后进行过滤
- this.noticeListNew = this.noticeListNew.filter(item=> item.messageId !== Number(msgId))
- if(this.noticeListNew.length > 0){
- this.customNotificationInstance.$set(
- this.customNotificationInstance,
- 'noticeList',
- [...this.noticeListNew]
- )
- this.customNotificationInstance.$forceUpdate()
- }else{
- this.$notify.closeAll()
- }
- }
- }
- })
- this.notificationInstance = this.$notify.info({
- title: '消息通知',
- message: notificationNode,
- customClass:'bellClass',
- position: 'bottom-right',
- duration:60000,
- onClose: () => {
- this.notificationInstance = null
- }
- });
- // 获取组件实例
- this.customNotificationInstance = notificationNode.componentInstance
- },
- // 处理列表更新事件
- handleListUpdate(newList) {
- this.noticeListNew = newList
- },
- //查看消息 弹窗显示
- readMessage(msgId) {
- const messageId = msgId;
- getSysmessage(messageId).then((response) => {
- this.messageVisible = true
- this.msg = response.data;
- // this.form.messageTitle = data.messageTitle
- // this.form.messageContent = data.messageContent
- if (this.msg.fileJSON && JSON.parse(this.msg.fileJSON)) {
- this.fileList = JSON.parse(this.msg.fileJSON);
- } else {
- this.fileList = [];
- }
- });
- },
- //批量已读
- readMessageAll(msgId) {
- const messageId = msgId;
- getSysmessage(messageId).then((response) => {
- });
- },
- getFileExtension(filename) {
- // 获取最后一个点的位置
- const lastDotIndex = filename.lastIndexOf('.')
- // 如果没有点或者点在第一个位置,返回空字符串
- if (lastDotIndex === -1 || lastDotIndex === 0) {
- return ''
- }
- // 截取点后的字符串作为后缀
- return filename.substring(lastDotIndex + 1)
- },
- closeMsgModel(){
- this.messageVisible = false
- }
- }
- };
- </script>
- <style rel="stylesheet/scss" lang="scss" scoped>
- .my-message {
- ::v-deep .el-checkbox__label {
- vertical-align: middle;
- }
- &__editor {
- ::v-deep .ql-toolbar {
- display: none;
- }
- ::v-deep .ql-container {
- // border-top: 1px solid #ccc !important;
- border: none !important;
- }
- ::v-deep .ql-editor {
- max-height: 500px;
- }
- }
- &__ul {
- list-style-type: none;
- // border: 1px solid #ccc;
- // border-radius: 5px;
- }
- &__filebox {
- margin-top: 10px;
- padding-left: 20px;
- padding-right: 20px;
- background: #e6f7ff;
- border-radius: 5px;
- }
- &__dialog {
- ::v-deep .el-dialog__body {
- padding-bottom: 0px !important;
- }
- }
- &__li {
- width: 100%;
- display: flex;
- justify-content: space-between;
- margin-bottom: 5px;
- &--opt {
- padding-right: 10px;
- }
- }
- }
- ::v-deep .el-badge__content.is-fixed {
- transform: translateY(-22%) translateX(100%);
- }
- ::v-deep .el-badge__content {
- font-size: 11px;
- padding: 0 5px;
- }
- ::v-deep .is-fullscreen {
- .el-dialog__body {
- /* padding: 15px 20px !important; */
- color: #606266;
- font-size: 14px;
- word-break: break-all;
- height: calc(100vh - 40px);
- overflow: auto;
- }
- }
- </style>
- <style>
- .bellClass .el-notification__icon::before {
- content: '\e7ba'; /* 需要更换的图标编码 */
- color: #8BC34A; /* 图标颜色 */
- }
- .bellClass {
- /*border: 1px solid #8bc34a57; !* 修改边框样式 f55e00 *!*/
- box-shadow: 0 2px 10px 0 rgb(0 0 0 / 48%) !important;
- }
- .el-notification__title {
- color: #000; /* 修改标题字体颜色 */
- font-size:12px;
- font-weight: 100;
- }
- .bellClass .el-notification__icon {
- height: 20px;
- width: 20px;
- font-size: 18px;
- transform: translateY(-2px);
- }
- .bellClass .el-notification__group {
- margin-left: 8px;
- margin-right: 8px;
- }
- </style>
复制代码 焦点代码:
这个表现关照的焦点方法, 自界说消息组件 通过$createElement创建Vnode挂载到message中,
数据转达的话通过props转达,通过on自界说变乱- showNotification() {
- // 如果已有通知实例且未关闭,先关闭
- if (this.notificationInstance) {
- this.$notify.closeAll()
- }
- const h = this.$createElement
- const notificationNode = h(CustomNotification, {
- key: this.notificationKey, // 添加唯一key
- props: {
- noticeList: [...this.noticeListNew], // 传递消息列表
- total:this.noticeListNew.length
- },
- on: {
- // 添加事件监听
- 'update-list': this.handleListUpdate,
- 'close-notification': () => this.$notify.closeAll(),
- 'later': () => {
- this.$notify.closeAll()
- },
- 'ignore-all': (localNoticeList) => {
- const msgList = localNoticeList
- msgList & msgList.forEach((item)=>{
- this.readMessageAll(item.messageId)
- })
- this.$notify.closeAll()
- this.getUnRead()
- },
- 'accept': () => {
- console.log('接受处理逻辑')
- },
- close: () => {
- this.notificationInstance = null
- },
- 'gotoMessageList': (msgId) => {
- this.readMessage(msgId)
- this.getUnRead()
- //阅读了那一条 根据ID匹配然后进行过滤
- this.noticeListNew = this.noticeListNew.filter(item=> item.messageId !== Number(msgId))
- if(this.noticeListNew.length > 0){
- this.customNotificationInstance.$set(
- this.customNotificationInstance,
- 'noticeList',
- [...this.noticeListNew]
- )
- this.customNotificationInstance.$forceUpdate()
- }else{
- this.$notify.closeAll()
- }
- }
- }
- })
- this.notificationInstance = this.$notify.info({
- title: '消息通知',
- message: notificationNode,
- customClass:'bellClass',
- position: 'bottom-right',
- duration:60000,
- onClose: () => {
- this.notificationInstance = null
- }
- });
- // 获取组件实例
- this.customNotificationInstance = notificationNode.componentInstance
- },
复制代码
获取当前弹窗的实例时数据通讯的关键! - /** 处理新消息逻辑 */
- handleNewMessages(rows, total) {
- // 数据同步
- this.noticeListNew = rows
- this.totalNew = rows.length//total
- // 仅当有新消息时触发通知
- if (rows.length > 0) {
- if (this.notificationInstance) {
- // 手动创建组件实例
- // 通过 key 变化强制更新组件
- this.notificationKey = Date.now()
- // const NotificationComponent = new Vue({
- // render: h => h(CustomNotification, {
- // props: {
- // noticeList: [...this.noticeListNew],
- // total: this.noticeListNew.length
- // },
- // on: {
- // // 事件监听...
- // }
- // })
- // }).$mount()
- // // 保存组件实例引用
- // this.customNotificationInstance = NotificationComponent.$children[0]
- // this.customNotificationInstance = this.notificationInstance.$children[0]
- //已有弹窗时仅更新数据
- this.customNotificationInstance.$set(
- this.customNotificationInstance,
- 'noticeList',
- [...rows]
- )
- this.customNotificationInstance.$forceUpdate()
- } else {
- this.showNotification()
- }
- }
- this.$emit('update:noticeList', rows)
- window.clearInterval(this.pollInterval)
- this.setupPolling()// 启动轮询 后台发送消息时 自动触发
- },
复制代码 已有弹窗只更新数据 不弹窗
|