Vue+Notification 自定义消息通知组件 支持数据分页 实时更新

news2025/4/20 21:18:17

效果图:


 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()// 启动轮询 后台发送消息时 自动触发
    },

已有弹窗只更新数据 不弹窗 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2338996.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不规则曲面上两点距离求取

背景 在CT中求皮肤上两点间的弧长。由于人体表面并不是规则的曲面&#xff0c;不可能用圆的弧长求取方法来计算出两点间的弧长。 而在不规则的曲面上求两点的距离&#xff0c;都可以用类似测地线距离求取的方式来求取&#xff08;积分&#xff09;&#xff0c;而转化为搜索路…

性能比拼: Elixir vs Go

本内容是对知名性能评测博主 Anton Putra Elixir vs Go (Golang) Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 对比 Elixir 和 Go 简介 许多人长期以来一直要求我对比 Elixir 和 Go。在本视频…

【Linux网络与网络编程】11.数据链路层mac帧协议ARP协议

前面在介绍网络层时我们提出来过一个问题&#xff1a;主机是怎么把数据交给路由器的&#xff1f;那里我们说这是由数据链路层来做的。 网络上的报文在物理结构上是以mac帧的形式流动的&#xff0c;但在逻辑上是以IP流动的&#xff0c;IP的流动是需要mac帧支持的。 数据链路层解…

lottie深入玩法

A、json文件和图片资源分开 delete 是json资源名字 /res/lottie/delete_anim_images是图片资源文件夹路径 JSON 中引用的图片名&#xff0c;必须与实际图片文件名一致 B、json文件和图片资源分开&#xff0c;并且图片加载不固定 比如我有7张图片&#xff0c;分别命名1~7&…

热门与冷门并存,25西电—电子工程学院(考研录取情况)

1、电子工程学院各个方向 2、电子工程学院近三年复试分数线对比 学长、学姐分析 由表可看出&#xff1a; 1、电子科学与技术25年相较于24年上升20分 2、信息与通信工程、控制科学与工程、新一代电子信息技术&#xff08;专硕&#xff09;25年相较于24年下降25分 3、25vs24推…

Warcraft Logs [Classic] [WCL] BOSS ID query

Warcraft Logs [Classic] [WCL] BOSS ID query 所有副本BOSSID查询 https://wowpedia.fandom.com/wiki/DungeonEncounterID#Retail IDNameMapInstanceIDPatch227High Interrogator GerstahnBlackrock Depths230228Lord RoccorBlackrock Depths230229Houndmaster GrebmarBlackro…

架构师面试(三十一):IM 消息收发逻辑

问题 今天聊一下 IM 系统最核心的业务逻辑。 在上一篇短文《架构师面试&#xff08;三十&#xff09;&#xff1a;IM 分层架构》中详细分析过&#xff0c;IM 水平分层架构包括&#xff1a;【入口网关层】、【业务逻辑层】、【路由层】和【数据访问层】&#xff1b;除此之外&a…

基于若依框架前后端分离的项目部署

文章目录 单项目的部署项目目录后端打包上传前端打包上传配置nginx服务器打开防火墙完成 两个项目的部署两个项目介绍后端打包并上传前端打包并上传nginx配置服务器端口开放完成 腾讯云服务器 之 环境搭建 单项目的部署 项目目录 后端打包上传 查看端口号 在ruoyi-admin的appl…

黑马Java基础笔记-1

JVM&#xff0c;JDK和JRE JDK是java的开发环境 JVM虚拟机&#xff1a;Java程序运行的地方 核心类库&#xff1a;Java已经写好的东西&#xff0c;我们可以直接用。 System.out.print中的这些方法就是核心库中的所包含的 开发工具: javac&#xff08;编译工具&#xff09;、java&…

面向新一代扩展现实(XR)应用的物联网框架

中文标题&#xff1a; 面向新一代扩展现实&#xff08;XR&#xff09;应用的物联网框架 英文标题&#xff1a; Towards an IoT Framework for the New Generation of XR Applications 作者信息 Joo A. Dias&#xff0c;UNIDCOM - IADE&#xff0c;欧洲大学&#xff0c;里斯本&…

pcl各模块

参考资料&#xff1a; https://github.com/Ewenwan/MVision/blob/master/PCL_APP/1_%E7%82%B9%E4%BA%91%E6%BB%A4%E6%B3%A2filter.md 点云库PCL各模块学习 语雀 各模块依赖关系&#xff1a; 模块&#xff1a; common pcl_common中主要是包含了PCL库常用的公共数据结构和方…

Oracle Recovery Tools修复ORA-600 6101/kdxlin:psno out of range故障

数据库异常断电,然后启动异常,我接手该库,尝试recover恢复 SQL> recover database; ORA-10562: Error occurred while applying redo to data block (file# 2, block# 63710) ORA-10564: tablespace SYSAUX ORA-01110: ???????? 2: H:\TEMP\GDLISNET\SYSAUX01.DBF O…

2025MathorcupC题 音频文件的高质量读写与去噪优化 保姆级教程讲解|模型讲解

2025Mathorcup数学建模挑战赛&#xff08;妈妈杯&#xff09;C题保姆级分析完整思路代码数据教学 C题&#xff1a;音频文件的高质量读写与去噪优化 随着数字媒体技术的迅速发展&#xff0c;音频处理成为信息时代的关键技术之一。在日常生活中&#xff0c;从录音设备捕捉的原始…

.net core web api 数据验证(DataAnnotations)

目录 一、什么是 DataAnnotations&#xff1f; 二、扩展验证逻辑&#xff08;自定义验证器&#xff09; 一、什么是 DataAnnotations&#xff1f; DataAnnotations 是一组特性&#xff08;Attributes&#xff09;&#xff0c;用于在模型类上定义验证规则。主要用于属性级别的…

【工具-Krillin AI】视频翻译、配音、语音克隆于一体的一站式视频多语言转换工具~

Krillin AI 是全能型音视频本地化与增强解决工具。这款简约而强大的工具&#xff0c;集音视频翻译、配音、语音克隆于一身&#xff0c;支持横竖屏格式输出&#xff0c;确保在所有主流平台&#xff08;哔哩哔哩&#xff0c;小红书&#xff0c;抖音&#xff0c;视频号&#xff0c…

ICPR-2025 | 让机器人在未知环境中 “听懂” 指令精准导航!VLTNet:基于视觉语言推理的零样本目标导航

作者&#xff1a;Congcong Wen, Yisiyuan Huang, Hao Huang ,Yanjia Huang, Shuaihang Yuan, YuHao, HuiLin and Yi Fang 单位&#xff1a;纽约大学阿布扎比分校具身人工智能与机器人实验室&#xff0c;纽约大学阿布扎比分校人工智能与机器人中心&#xff0c;纽约大学坦登工程…

Shiro-550 动调分析与密钥正确性判断

一、Shiro 简介 Apache Shiro是一个开源安全框架&#xff0c;用于构建 Java 应用程序&#xff0c;提供身份验证、授权、加密和会话管理等功能。 二、Shiro-550&#xff08;CVE-2016-4437&#xff09; 1、漏洞原理 Shiro 在用户登陆时提供可选项 RememberMe&#xff0c;若勾选…

Python制作简易PDF查看工具PDFViewerV1.0查找功能优化

原文说明 为不破坏原文结构&#xff0c;因此功能优化不在原文中维护了。关于这款工具原文请通过下面链接访问。Python制作简易PDF查看工具PDFViewerV1.0 这款小工具基本功能已经可以作为一款文档浏览器使用&#xff0c;但还有一些美中不足的地方&#xff0c;本文将介绍对文本查…

20250419将405的机芯由4LANE的LVDS OUT配置为8LANE的步骤

20250419将405的机芯由4LANE的LVDS OUT配置为8LANE的步骤 2025/4/19 15:38 查询格式YUV/RGB 81 09 04 24 60 FF 90 50 00 00 FF 查询辨率帧率 81 09 04 24 72 FF 90 50 01 03 FF 查询LVDS mode : Singel output/Dual output 81 09 04 24 74 FF 90 50 00 00 FF 配置405的机…

从0开发一个unibest+vue3项目,使用vscode编辑器开发,总结vue2升vue3项目开始,小白前期遇到的问题

开头运行可看官网 链接: unibest官网 一&#xff1a;vscode中vue3代码显示报错标红波浪线 去查看扩展商店发现一些插件都弃用了&#xff0c;例如h5的插件以及vue老插件 解决办法&#xff1a;下载Vue - Official插件&#xff08;注意&#xff1a;横杠两边是要加空格的&#xff…