【Vue】vue增加导航标签

系列文章

【C#】WebAPI,在Windows IIS平台部署
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126539836

【Vue】vue,在Windows IIS平台部署
本文链接:https://blog.csdn.net/youcheng_ge/article/details/133859117

【Vue】vue2与WebApi跨域CORS问题
本文链接:https://blog.csdn.net/youcheng_ge/article/details/133808959

文章目录

  • 系列文章
  • 前言
  • 一、技术介绍
  • 二、项目源码
    • 2.1 布局增加TagsView
    • 2.2 components添加引用
    • 2.3 layout添加引用
    • 2.4 AppMain添加引用
    • 2.5 AppMain样式调整
    • 2.6 store中的tagsView.js
    • 2.7 getters.js添加引用
    • 2.8 store index.js添加引用
    • 2.9 首页固钉
  • 三、效果展示
  • 四、异常问题
    • 4.1 导航标签,刷页面报404
    • 4.2 页面高度无法全屏,出现滚动条或遮挡
  • 五、资源链接


前言

本专栏为【Vue】,主要介绍前端知识点。TagsView 导航标签,如何实现???
在这里插入图片描述

一、技术介绍

最初学习使用 vue Admin Template模板,后项目成型后,完善功能,需要添加头部 快捷导航标签,效果如下图:
在这里插入图片描述
本文介绍就是在 vue Admin Template模板基础上如何实现这一效果。

二、项目源码

2.1 布局增加TagsView

布局组件中,增加标签视图TagsView ,名字自己定义的,建议 TagsView 比较通用,官方集成版本也是这个名称。

位置:layout\components\TagsView
文件:ScrollPane.vue

<template>
  <el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
    <slot />
  </el-scrollbar>
</template>

<script>
const tagAndTagSpacing = 4 // tagAndTagSpacing

export default {
  name: 'scroll-pane',
  data() {
    return {
      left: 0
    }
  },
  computed: {
    scrollWrapper() {
      return this.$refs.scrollContainer.$refs.wrap
    }
  },
  methods: {
    handleScroll(e) {
      const eventDelta = e.wheelDelta || -e.deltaY * 40
      const $scrollWrapper = this.scrollWrapper
      $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
    },
    moveToTarget(currentTag) {
      const $container = this.$refs.scrollContainer.$el
      const $containerWidth = $container.offsetWidth
      const $scrollWrapper = this.scrollWrapper
      const tagList = this.$parent.$refs.tag

      let firstTag = null
      let lastTag = null

      // find first tag and last tag
      if (tagList.length > 0) {
        firstTag = tagList[0]
        lastTag = tagList[tagList.length - 1]
      }

      if (firstTag === currentTag) {
        $scrollWrapper.scrollLeft = 0
      } else if (lastTag === currentTag) {
        $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
      } else {
        // find preTag and nextTag
        const currentIndex = tagList.findIndex((item) => item === currentTag)
        const prevTag = tagList[currentIndex - 1]
        const nextTag = tagList[currentIndex + 1]

        // the tag's offsetLeft after of nextTag
        const afterNextTagOffsetLeft =
          nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing

        // the tag's offsetLeft before of prevTag
        const beforePrevTagOffsetLeft =
          prevTag.$el.offsetLeft - tagAndTagSpacing

        if (
          afterNextTagOffsetLeft >
          $scrollWrapper.scrollLeft + $containerWidth
        ) {
          $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
        } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
          $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
        }
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.scroll-container {
  white-space: nowrap;
  position: relative;
  overflow: hidden;
  width: 100%;
  ::v-deep {
    .el-scrollbar__bar {
      bottom: 0px;
    }
    .el-scrollbar__wrap {
      height: 49px;
    }
  }
}
</style>

刚刚添加的文件,需要在引入(暴露)出来,所以需要增加 index.vue文件,并且添加引入。
提示:为什么一定要命名为 index,可以了解一下vue引入机制,以及 目录、*.js、*.vue优先级很好明白

位置:layout\components\TagsView
文件:index.vue

<template>
  <div id="tags-view-container" class="tags-view-container">
    <scroll-pane ref="scrollPane" class="tags-view-wrapper">
      <router-link v-for="tag in visitedViews" ref="tag" :key="tag.path" :class="isActive(tag)?'active':''" :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" tag="span" class="tags-view-item" @click.middle.native="closeSelectedTag(tag)" @contextmenu.prevent.native="openMenu(tag,$event)">
        {{ tag.title }}
        <span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
      </router-link>
    </scroll-pane>
    <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
      <li @click="refreshSelectedTag(selectedTag)">刷新</li>
      <li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">关闭</li>
      <li @click="closeOthersTags">关闭其他</li>
      <li @click="closeAllTags(selectedTag)">关闭全部</li>
    </ul>
  </div>
</template>

<script>
import ScrollPane from './ScrollPane'
import path from 'path'

export default {
  components: { ScrollPane },
  data() {
    return {
      visible: false,
      top: 0,
      left: 0,
      selectedTag: {},
      affixTags: []
    }
  },
  computed: {
    visitedViews() {
      return this.$store.state.tagsView.visitedViews
    },
    routes() {
      // gyc 修改此处
      // return this.$store.state.routers.routers 
      return this.$router.options.routes
    }
  },
  watch: {
    $route() {
      this.addTags()
      this.moveToCurrentTag()
    },
    visible(value) {
      if (value) {
        document.body.addEventListener('click', this.closeMenu)
      } else {
        document.body.removeEventListener('click', this.closeMenu)
      }
    }
  },
  mounted() {
    this.initTags()
    this.addTags()
  },
  methods: {
    isActive(route) {
      return route.path === this.$route.path
    },
    filterAffixTags(routes, basePath = '/') {
      let tags = []
      routes.forEach(route => {
        if (route.meta && route.meta.affix) {
          const tagPath = path.resolve(basePath, route.path)
          tags.push({
            fullPath: tagPath,
            path: tagPath,
            name: route.name,
            meta: { ...route.meta }
          })
        }
        if (route.children) {
          const tempTags = this.filterAffixTags(route.children, route.path)
          if (tempTags.length >= 1) {
            tags = [...tags, ...tempTags]
          }
        }
      })
      return tags
    },
    initTags() {
      const affixTags = (this.affixTags = this.filterAffixTags(this.routes))
      for (const tag of affixTags) {
        // Must have tag name
        if (tag.name) {
          this.$store.dispatch('tagsView/addVisitedView', tag)
        }
      }
    },
    addTags() {
      const { name } = this.$route
      if (name) {
        this.$store.dispatch('tagsView/addView', this.$route)
      }
      return false
    },
    moveToCurrentTag() {
      const tags = this.$refs.tag
      this.$nextTick(() => {
        for (const tag of tags) {
          if (tag.to.path === this.$route.path) {
            this.$refs.scrollPane.moveToTarget(tag)
            // when query is different then update
            if (tag.to.fullPath !== this.$route.fullPath) {
              this.$store.dispatch('tagsView/updateVisitedView', this.$route)
            }
            break
          }
        }
      })
    },
    refreshSelectedTag(view) {
      this.$store.dispatch('tagsView/delCachedView', view).then(() => {
        const { fullPath } = view
        this.$nextTick(() => {
          this.$router.replace({
            path: '/redirect' + fullPath
          })
        })
      })
    },
    closeSelectedTag(view) {
      this.$store
        .dispatch('tagsView/delView', view)
        .then(({ visitedViews }) => {
          if (this.isActive(view)) {
            this.toLastView(visitedViews, view)
          }
        })
    },
    closeOthersTags() {
      this.$router.push(this.selectedTag)
      this.$store
        .dispatch('tagsView/delOthersViews', this.selectedTag)
        .then(() => {
          this.moveToCurrentTag()
        })
    },
    closeAllTags(view) {
      this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
        if (this.affixTags.some(tag => tag.path === view.path)) {
          return
        }
        this.toLastView(visitedViews, view)
      })
    },
    toLastView(visitedViews, view) {
      const latestView = visitedViews.slice(-1)[0]
      if (latestView) {
        this.$router.push(latestView)
      } else {
        // now the default is to redirect to the home page if there is no tags-view,
        // you can adjust it according to your needs.
        if (view.name === 'Dashboard') {
          // to reload home page
          this.$router.replace({ path: '/redirect' + view.fullPath })
        } else {
          this.$router.push('/')
        }
      }
    },
    openMenu(tag, e) {
      const menuMinWidth = 105
      const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
      const offsetWidth = this.$el.offsetWidth // container width
      const maxLeft = offsetWidth - menuMinWidth // left boundary
      const left = e.clientX - offsetLeft + 15 // 15: margin right

      if (left > maxLeft) {
        this.left = maxLeft
      } else {
        this.left = left
      }

      this.top = e.clientY
      this.visible = true
      this.selectedTag = tag
    },
    closeMenu() {
      this.visible = false
    }
  }
}
</script>

<style lang="scss" scoped>
.tags-view-container {
  height: 34px;
  width: 100%;
  background: #fff;
  border-bottom: 1px solid #d8dce5;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
  .tags-view-wrapper {
    .tags-view-item {
      display: inline-block;
      position: relative;
      cursor: pointer;
      height: 26px;
      line-height: 26px;
      border: 1px solid #d8dce5;
      color: #495060;
      background: #fff;
      padding: 0 8px;
      font-size: 12px;
      margin-left: 5px;
      margin-top: 4px;
      &:first-of-type {
        margin-left: 15px;
      }
      &:last-of-type {
        margin-right: 15px;
      }
      &.active {
        background-color: #42b983;
        color: #fff;
        border-color: #42b983;
        &::before {
          content: '';
          background: #fff;
          display: inline-block;
          width: 8px;
          height: 8px;
          border-radius: 50%;
          position: relative;
          margin-right: 2px;
        }
      }
    }
  }
  .contextmenu {
    margin: 0;
    background: #fff;
    z-index: 3000;
    position: absolute;
    list-style-type: none;
    padding: 5px 0;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 400;
    color: #333;
    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
    li {
      margin: 0;
      padding: 7px 16px;
      cursor: pointer;
      &:hover {
        background: #eee;
      }
    }
  }
}
</style>

<style lang="scss">
//reset element css of el-icon-close
.tags-view-wrapper {
  .tags-view-item {
    .el-icon-close {
      width: 16px;
      height: 16px;
      vertical-align: 2px;
      border-radius: 50%;
      text-align: center;
      transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
      transform-origin: 100% 50%;
      &:before {
        transform: scale(0.6);
        display: inline-block;
        vertical-align: -3px;
      }
      &:hover {
        background-color: #b4bccc;
        color: #fff;
      }
    }
  }
}
</style>

路由需要稍加修改,由于我项目没有使用动态路由,所以也没有promise权限验证、也没有在store缓存,所以需要改成如下图:
在这里插入图片描述

2.2 components添加引用

位置:src\layout\components\index.js
代码:export { default as TagsView } from ‘./TagsView’

在这里插入图片描述

2.3 layout添加引用

位置:src\layout\index.vue
在这里插入图片描述

2.4 AppMain添加引用

位置:src\layout\components\AppMain.vue
代码:

   <keep-alive :include="cachedViews">
     <router-view></router-view>
   </keep-alive>
   
 // gyc 添加
 cachedViews() {
   return this.$store.state.tagsView.cachedViews
 },

在这里插入图片描述

2.5 AppMain样式调整

问题原因:由于我们布局上增加了一行导航标签,会导致整个页面下移、或者遮挡。
这很好理解,比如说:原本 高度1080像素的页面,全屏显示正好,你增加了34像素的导航标签,导致整个页面高度变成1114像素,这时候全屏放不下了,会出现滚动条,很不美观。
所以,就要让出这部分高度 84=50+34像素

// gyc 让出导航标签高度 34
.hasTagsView {
  .app-main {
    min-height: calc(100vh - 84px);
  }

  .fixed-header+.app-main {
    padding-top: 84px;
  }
}

2.6 store中的tagsView.js

位置:src\store\modules\tagsView.js
代码:源码有 cachedViews和visitedViews两个数组

//  @desc 张家口裸砂MES
//  @author gyc
//  @date 2023-11-23
//  @note 标签栏导航

const state = {
  visitedViews: [],
  cachedViews: []
}

const mutations = {
  ADD_VISITED_VIEW: (state, view) => {
    if (state.visitedViews.some(v => v.path === view.path)) return
    state.visitedViews.push(
      Object.assign({}, view, {
        title: view.meta.title || 'no-name'
      })
    )
  },
  ADD_CACHED_VIEW: (state, view) => {
    if (state.cachedViews.includes(view.meta.path)) return
    if (view.meta.keepAlive) {
      state.cachedViews.push(view.meta.path)
    }
  },

  DEL_VISITED_VIEW: (state, view) => {
    for (const [i, v] of state.visitedViews.entries()) {
      if (v.path === view.path) {
        state.visitedViews.splice(i, 1)
        break
      }
    }
  },
  DEL_CACHED_VIEW: (state, view) => {
    for (const i of state.cachedViews) {
      if (i === view.meta.path) {
        const index = state.cachedViews.indexOf(i)
        state.cachedViews.splice(index, 1)
        break
      }
    }
  },

  DEL_OTHERS_VISITED_VIEWS: (state, view) => {
    state.visitedViews = state.visitedViews.filter(v => {
      return v.meta.affix || v.path === view.path
    })
  },
  DEL_OTHERS_CACHED_VIEWS: (state, view) => {
    for (const i of state.cachedViews) {
      if (i === view.meta.path) {
        const index = state.cachedViews.indexOf(i)
        state.cachedViews = state.cachedViews.slice(index, index + 1)
        break
      }
    }
  },

  DEL_ALL_VISITED_VIEWS: state => {
    // keep affix tags
    const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
    state.visitedViews = affixTags
  },
  DEL_ALL_CACHED_VIEWS: state => {
    state.cachedViews = []
  },

  UPDATE_VISITED_VIEW: (state, view) => {
    for (let v of state.visitedViews) {
      if (v.path === view.path) {
        v = Object.assign(v, view)
        break
      }
    }
  }
}

const actions = {
  addView({ dispatch }, view) {
    dispatch('addVisitedView', view)
    dispatch('addCachedView', view)
  },
  addVisitedView({ commit }, view) {
    commit('ADD_VISITED_VIEW', view)
  },
  addCachedView({ commit }, view) {
    commit('ADD_CACHED_VIEW', view)
  },

  delView({ dispatch, state }, view) {
    return new Promise(resolve => {
      dispatch('delVisitedView', view)
      dispatch('delCachedView', view)
      resolve({
        visitedViews: [...state.visitedViews],
        cachedViews: [...state.cachedViews]
      })
    })
  },
  delVisitedView({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_VISITED_VIEW', view)
      resolve([...state.visitedViews])
    })
  },
  delCachedView({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_CACHED_VIEW', view)
      resolve([...state.cachedViews])
    })
  },

  delOthersViews({ dispatch, state }, view) {
    return new Promise(resolve => {
      dispatch('delOthersVisitedViews', view)
      dispatch('delOthersCachedViews', view)
      resolve({
        visitedViews: [...state.visitedViews],
        cachedViews: [...state.cachedViews]
      })
    })
  },
  delOthersVisitedViews({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_OTHERS_VISITED_VIEWS', view)
      resolve([...state.visitedViews])
    })
  },
  delOthersCachedViews({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_OTHERS_CACHED_VIEWS', view)
      resolve([...state.cachedViews])
    })
  },

  delAllViews({ dispatch, state }, view) {
    return new Promise(resolve => {
      dispatch('delAllVisitedViews', view)
      dispatch('delAllCachedViews', view)
      resolve({
        visitedViews: [...state.visitedViews],
        cachedViews: [...state.cachedViews]
      })
    })
  },
  delAllVisitedViews({ commit, state }) {
    return new Promise(resolve => {
      commit('DEL_ALL_VISITED_VIEWS')
      resolve([...state.visitedViews])
    })
  },
  delAllCachedViews({ commit, state }) {
    return new Promise(resolve => {
      commit('DEL_ALL_CACHED_VIEWS')
      resolve([...state.cachedViews])
    })
  },

  updateVisitedView({ commit }, view) {
    commit('UPDATE_VISITED_VIEW', view)
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

2.7 getters.js添加引用

位置:src\store\getters.js
代码:

  // gyc 添加 引入
  visitedViews: state => state.tagsView.visitedViews,
  cachedViews: state => state.tagsView.cachedViews

2.8 store index.js添加引用

位置:src\store\index.js
代码:

// gyc 添加
import tagsView from './modules/tagsView'

const store = new Vuex.Store({
  modules: {
    app,
    settings,
    user,
    // gyc 添加
    tagsView
  },
  getters
})

2.9 首页固钉

我们可以发现,导航标签有些 带有关闭的小叉号,但是首页就没有。这是如何实现的?是采用了
固钉属性,在路由中配置的。
位置:router\index.js
代码:affix: true

在这里插入图片描述

三、效果展示

在这里插入图片描述
在这里插入图片描述

四、异常问题

4.1 导航标签,刷页面报404

导致原因:refreshSelectedTag()发现他会在你当前路由前面加上一个/redirect,导致找不到路由。
处理思路:URL重写呗。

①处理方式一:发布部署时(IIS),配置路由重写(路由转发)。
以前文章有所介绍,阅读文章《【Vue】vue,在Windows IIS平台部署》,第 3.3 配置URL重写

②处理方式二:Vue项目中处理,增加 redirect路由重写,说白了就是路由文本级别的替换。

位置:views\redirect\index.vue

<!-- 
 @desc 张家口裸砂MES
 @author gyc
 @date 2023-12-13
 @note 页面重定向,删除报错概不负责
 -->
<script>
export default {
  created() {
    const { params, query } = this.$route
    const { path } = params
    this.$router.replace({ path: '/' + path, query })
  },
  render: function(h) {
    return h() // avoid warning message
  }
}
</script>

位置:router\index.js

  {
    path: '/redirect',
    component: Layout,
    hidden: true,
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index')
      }
    ]
  },

4.2 页面高度无法全屏,出现滚动条或遮挡

按2.5节处理

五、资源链接

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker compose部署wordpress

准备机器&#xff1a; 192.168.58.151 &#xff08;关闭防火墙和selinux&#xff09; 安装好docker服务 &#xff08;详细参照&#xff1a;http://t.csdnimg.cn/usG0s 中的国内源安装docker&#xff09; 部署wordpress: 创建目录&#xff1a; [rootdocker ~]# mkdir…

Selenium库自动化测试入门

前言 为什么要学selenium&#xff1f;&#xff1f;前面已经学了requests库我们会发现 对于绝大多数动态渲染的网页来说&#xff0c;用requests进行爬虫比较繁琐。 所以我们还是要学习一下selenium库&#xff0c;以帮助我们更高效的爬取网页。 环境&#xff1a; pychar 202…

flutter调试器查看不了副页面(非主页面/子页面)

刚接触flutter&#xff0c;写了两个页面&#xff0c;通过按钮&#xff0c;可以从主页面跳转到副页面&#xff0c;副页面我自己写的一个独立的dart文件&#xff0c;在主页面的代码中导入使用。但是当我运行代码后&#xff0c;点击跳转的时候&#xff0c;却发现查看不到对应的副页…

Linux驱动入门 —— 利用引脚号操作GPIO进行LED点灯

目录 一、字符设备驱动程序框架 编写驱动程序的步骤&#xff1a; 对于 LED 驱动&#xff0c;我们想要什么样的接口&#xff1f; LED 驱动能支持多个板子的基础&#xff1a;分层思想 二、Linux驱动如何指向一个GPIO 直接通过寄存器来操作GPIO 利用引脚号操作GPIO IMX6UL…

STM32的看门狗(WDG)

WDG&#xff08;Watchdog&#xff09;看门狗 看门狗可以监控程序的运行状态&#xff0c;当程序因为设计漏洞、硬件故障、电磁干扰等原因&#xff0c;出现卡死或跑飞现象时&#xff0c;看门狗能及时复位程序&#xff0c;避免程序陷入长时间的罢工状态&#xff0c;保证系统的可靠…

基于C/C++的rapidxml加载xml大文件 - 下部分

下载地址: RapidXml (sourceforge.net)https://rapidxml.sourceforge.net/ 将源码添加到自己的工程中 示例测试大文件耗时: 总共293w行数据&#xff0c;大概耗时不到1s。

Paper Reading: (U2PL) 基于不可靠伪标签的半监督语义分割

目录 简介目标/动机方法Pseudo-LabelingUsing Unreliable Pseudo-Labels 补充知识InfoNCE LossOHEM 实验Comparison with Existing AlternativesAblationEffectiveness of Using Unreliable Pseudo-LabelsAlternative of Contrastive Learning 总结附录U2PL 与 negative learni…

【C语言程序设计】数组程序设计

目录 前言 一、数组的定义和初始化 二、数组的基本操作 三、数组的高级应用 四、程序设计 4.1 程序设计第一题 4.2 程序设计第二题 4.3 程序设计第三题 总结 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助…

论文阅读《DPS-Net: Deep Polarimetric Stereo Depth Estimation》

论文地址&#xff1a;https://openaccess.thecvf.com/content/ICCV2023/html/Tian_DPS-Net_Deep_Polarimetric_Stereo_Depth_Estimation_ICCV_2023_paper.html 概述 立体匹配模型难以处理无纹理场景的匹配&#xff0c;现有的方法通常假设物体表面是光滑的&#xff0c;或者光照是…

设计模式(2)--对象创建(4)--原型

1. 意图 用原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。 2. 两种角色 抽象原型(Prototype)、具体原型(Concrete Prototype) 3. 优点 3.1 对客户隐藏了具体的产品类 3.2 可以在运行时刻增加和删除产品 3.3 可以极大地减少系统所需要的类的数目 …

Weblogic-CVE-2023-21839

一、漏洞概述 RCE漏洞&#xff0c;该漏洞允许未经身份验证的远程&#xff0c;通过T3/IIOP协议网络访问并破坏WebLogic服务器&#xff0c;成功利用此漏洞可导致Oracle WebLogic服务器被接管&#xff0c;通过rmi/ldap远程协议进行远程命令执行,当 JDK 版本过低或本地存在小工具&…

@Scheduled任务调度/定时任务-非分布式

1、功能概述 任务调度就是在规定的时间内执行的任务或者按照固定的频率执行的任务。是非常常见的功能之一。常见的有JDK原生的Timer, ScheduledThreadPoolExecutor以及springboot提供的Schduled。分布式调度框架如QuartZ、Elasticjob、XXL-JOB、SchedulerX、PowerJob等。 本文…

出现 ‘mvn‘ 不是内部或外部命令,也不是可运行的程序或批处理文件 的解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 下载了Maven,也配置了环境,在环境变量中配置MAVEN_HOME,在用户变量中配置了bin变量 具体如下所示: 用户变量的配置: 结果显示如下所示: 2. 原理分析 HOME变量中会具体到jre变量,如果在用户变量中配置,jre可能…

SpringBoot+Vue3前后端快速整合入门

前言 最近需要维护一个个人项目&#xff0c;由于笔者是一个大后端&#xff0c;所以借此机会把前端学习过程记录一下&#xff0c;方便后续回顾。 前端项目初始化 安装npm 在前端项目初始化时&#xff0c;我们必须要安装好node&#xff0c;官网地址如下&#xff0c;因为笔者后…

数据结构实验任务八:排序算法的实现与分析

问题描述 统计成绩&#xff1a;给出 n 个学生的考试成绩表&#xff0c;每条信息由姓名和分数组成&#xff0c;试设 计一个算法&#xff1a; 1.按分数高低次序&#xff0c;打印出每个学生在考试中获得的名次&#xff0c;分数相同的为同 一名次&#xff1b; 2.按名次列出每个学生…

36V/48V转12V 10A直流降压DC-DC芯片-AH1007

AH1007是一款36V/48V转12V 10A直流降压&#xff08;DC-DC&#xff09;芯片&#xff0c;它是一种高性能的降压变换器&#xff0c;常用于工业、汽车和电子设备等领域。 AH1007采用了先进的PWM调制技术和开关电源控制算法&#xff0c;能够高效地将输入电压从36V/48V降低到12V&…

【Spark精讲】Spark内存管理

目录 前言 Java内存管理 Java运行时数据区 Java堆 垃圾回收机制 Executor内存管理 内存类型 堆内内存 堆外内存 内存管理模式 静态内存管理 统一内存管理 ​编辑 执行内存管理 多任务间内存分配 Shuffle 的内存占用 MemoryOverHead详解 任务内存调节 错误类型…

TCP/IP 四层体系结构

目录 ​编辑 导言&#xff1a; 应用层 1. HTTP&#xff08;超文本传输协议&#xff09; 2. FTP&#xff08;文件传输协议&#xff09; 3. SMTP&#xff08;简单邮件传输协议&#xff09; 4. POP3&#xff08;邮局协议第3版&#xff09; 5. IMAP&#xff08;互联网消息访…

群晖(Synology)新建存储池使用 Home 服务

每一个用户都可以有一个自己的 Home 服务。 这个在群晖存储新建存储池后可以自动启用这个服务。 启用后&#xff0c;可以看到你的文件系统中有一个 homes 的文件了。 群晖&#xff08;Synology&#xff09;新建存储池使用 Home 服务 - 系统容器 - iSharkFly每一个用户都可以有…

尚硅谷JavaWeb电子书城项目(Java+Mysql+Tomcat+Jsp)

自己写的在线电子书城项目&#xff0c;可改写&#xff0c;添加功能&#xff0c;如打折&#xff0c;分类&#xff0c;用户管理&#xff0c;评论等功能。 使用方法&#xff1a; 1.使用idea导入项目。 2.数据库要用项目resource文件里的book.sql文件建立。 3.修改jdbc.properi…