vue3-element-admin实现同一个菜单多标签

原框架代码: 赵志江/huzhushan-vue3-element-admin

目录

TagsBar实现

实现同一个菜单多标签

device/detail/:id,不同参数时页面缓存删不掉的问题


TagsBar实现

在src/layout/components/下新建目录Tagsbar,新建index.vue

<template><div class="tags-container" :class="{ hide: !isTagsbarShow }"><el-scrollbarref="scrollContainer":vertical="false"class="scroll-container"@wheel.prevent="onScroll"><router-linkv-for="(tag, i) in tagList":key="tag.fullPath":to="tag":ref="el => setItemRef(i, el)"customv-slot="{ navigate, isExactActive }"><divclass="tags-item":class="isExactActive? 'active' : ''"@click="navigate"@click.middle="closeTag(tag)"@contextmenu.prevent="openMenu(tag, $event)"><span class="title">{{ $t(tag.title) }}</span><el-iconv-if="!isAffix(tag)"class="el-icon-close"@click.prevent.stop="closeTag(tag)"><Close /></el-icon></div></router-link></el-scrollbar></div><ulv-show="visible":style="{ left: left + 'px', top: top + 'px' }"class="contextmenu"><!-- <li @click="refreshSelectedTag(selectedTag)">{{ $t('tags.refresh') }}</li> --><li v-if="!isAffix(selectedTag)" @click="closeTag(selectedTag)">{{ $t('tags.close') }}</li><li @click="closeOtherTags">{{ $t('tags.other') }}</li><li @click="closeLeftTags">{{ $t('tags.left') }}</li><li @click="closeRightTags">{{ $t('tags.right') }}</li><li @click="closeAllTags">{{ $t('tags.all') }}</li></ul>
</template><script>
import { defineComponent, computed, getCurrentInstance } from 'vue'
import { useTags } from './hooks/useTags'
import { useContextMenu } from './hooks/useContextMenu'
import { useLayoutsettings } from '@/pinia/modules/layoutSettings'export default defineComponent({name: 'Tagsbar',mounted() {},setup() {const instance = getCurrentInstance()instance.appContext.config.globalProperties.$tagsbar = thisconst defaultSettings = useLayoutsettings()const isTagsbarShow = computed(() => defaultSettings.tagsbar.isShow)const tags = useTags()const contextMenu = useContextMenu(tags.tagList)const onScroll = e => {tags.handleScroll(e)contextMenu.closeMenu.value()}return {isTagsbarShow,onScroll,...tags,...contextMenu}},
})
</script><style lang="scss" scoped>
.tags-container {height: 32px;width: 100%;background: #fff;border-bottom: 1px solid #e0e4ef;&.hide {display: none;}.scroll-container {white-space: nowrap;overflow: hidden;::v-deep(.el-scrollbar__bar) {bottom: 0px;}}.tags-item {display: inline-block;height: 32px;line-height: 32px;box-sizing: border-box;border-left: 1px solid #e6e6e6;border-right: 1px solid #e6e6e6;color: #5c5c5c;background: #fff;padding: 0 8px;font-size: 12px;margin-left: -1px;vertical-align: bottom;cursor: pointer;&:first-of-type {margin-left: 15px;}&:last-of-type {margin-right: 15px;}&.active {color: #303133;background: #f5f5f5;}.title {display: inline-block;vertical-align: top;max-width: 200px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}.el-icon-close {color: #5c5c5c;margin-left: 8px;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.8);display: inline-block;vertical-align: -2px;}&:hover {background-color: #333;color: #fff;}}}
}
.contextmenu {margin: 0;background: #fff;z-index: 3000;position: fixed;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);white-space: nowrap;li {margin: 0;padding: 8px 16px;cursor: pointer;&:hover {background: #eee;}}
}
</style>

新建hooks目录,新建useTags.js

import { storeToRefs } from 'pinia'
import { useTags as useTagsbar } from '@/pinia/modules/tags'
import { useScrollbar } from './useScrollbar'
import { watch, computed, ref, nextTick, onBeforeMount } from 'vue'
import { useRouter } from 'vue-router'export const isAffix = tag => {return !!tag.meta && !!tag.meta.affix
}export const useTags = () => {const tagStore = useTagsbar()const { tagList } = storeToRefs(tagStore)const { addTag, delTag, saveActivePosition, updateTagList } = tagStoreconst router = useRouter()const route = router.currentRouteconst routes = computed(() => router.getRoutes())const tagsItem = ref([])const setItemRef = (i, el) => {tagsItem.value[i] = el}const scrollbar = useScrollbar(tagsItem)watch(() => tagList.value.length,() => {tagsItem.value = []})const filterAffixTags = routes => {return routes.filter(route => isAffix(route))}const initTags = () => {const affixTags = filterAffixTags(routes.value)for (const tag of affixTags) {if (tag.name) {addTag(tag)}}// 不在路由中的所有标签,需要删除const noUseTags = tagList.value.filter(tag =>routes.value.every(route => route.name !== tag.name))noUseTags.forEach(tag => {delTag(tag)})}const addTagList = () => {const tag = route.valueif (!!tag.name && tag.matched[0].components.default.name === 'layout') {addTag(tag)}}const saveTagPosition = tag => {const index = tagList.value.findIndex(item => item.fullPath === tag.fullPath)saveActivePosition(Math.max(0, index))}const moveToCurrentTag = () => {nextTick(() => {for (const tag of tagsItem.value) {if (!!tag && tag.to.path === route.value.path) {scrollbar.moveToTarget(tag)if (tag.to.fullPath !== route.value.fullPath) {updateTagList(route.value)}break}}})}onBeforeMount(() => {initTags()addTagList()moveToCurrentTag()})watch(route, (newRoute, oldRoute) => {saveTagPosition(oldRoute) // 保存标签的位置addTagList()moveToCurrentTag()})return {tagList,setItemRef,isAffix,...scrollbar,}
}

useScrollbar.js

import { ref } from 'vue'export const useScrollbar = tagsItem => {const scrollContainer = ref(null)const scrollLeft = ref(0)const doScroll = val => {scrollLeft.value = valscrollContainer.value.setScrollLeft(scrollLeft.value)}const handleScroll = e => {const $wrap = scrollContainer.value.wrapRefif ($wrap.offsetWidth + scrollLeft.value > $wrap.children[0].scrollWidth) {doScroll($wrap.children[0].scrollWidth - $wrap.offsetWidth)return} else if (scrollLeft.value < 0) {doScroll(0)return}const eventDelta = e.wheelDelta || -e.deltaYdoScroll(scrollLeft.value - eventDelta / 4)}const moveToTarget = currentTag => {const $wrap = scrollContainer.value.wrapRefconst tagList = tagsItem.valuelet firstTag = nulllet lastTag = nullif (tagList.length > 0) {firstTag = tagList[0]lastTag = tagList[tagList.length - 1]}if (firstTag === currentTag) {doScroll(0)} else if (lastTag === currentTag) {doScroll($wrap.children[0].scrollWidth - $wrap.offsetWidth)} else {const el = currentTag.$el.nextElementSiblingel.offsetLeft + el.offsetWidth > $wrap.offsetWidth? doScroll(el.offsetLeft - el.offsetWidth): doScroll(0)}}return {scrollContainer,handleScroll,moveToTarget,}
}

useContextMenu.js

import { useTags } from '@/pinia/modules/tags'
import { onMounted, onBeforeUnmount, reactive, toRefs, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { isAffix } from './useTags'export const useContextMenu = tagList => {const router = useRouter()const route = useRoute()const tagsStore = useTags()const state = reactive({visible: false,top: 0,left: 0,selectedTag: {},openMenu(tag, e) {state.visible = truestate.left = e.clientXstate.top = e.clientYstate.selectedTag = tag},closeMenu() {state.visible = false},refreshSelectedTag(tag) {tagsStore.deCacheList(tag)const { fullPath } = tagnextTick(() => {router.replace({path: '/redirect' + fullPath,})})},closeTag(tag) {if (isAffix(tag)) returnconst closedTagIndex = tagList.value.findIndex(item => {return item.path === tag.path})console.log(closedTagIndex)tagsStore.delTag(tag)if (isActive(tag)) {toLastTag(closedTagIndex - 1)}},closeOtherTags() {tagsStore.delOtherTags(state.selectedTag)router.push(state.selectedTag)},closeLeftTags() {state.closeSomeTags('left')},closeRightTags() {state.closeSomeTags('right')},closeSomeTags(direction) {const index = tagList.value.findIndex(item => item.fullPath === state.selectedTag.fullPath)if ((direction === 'left' && index <= 0) ||(direction === 'right' && index >= tagList.value.length - 1)) {return}const needToClose =direction === 'left'? tagList.value.slice(0, index): tagList.value.slice(index + 1)tagsStore.delSomeTags(needToClose)router.push(state.selectedTag)},closeAllTags() {tagsStore.delAllTags()router.push('/')},})const isActive = tag => {return tag.fullPath === route.fullPath}const toLastTag = lastTagIndex => {const lastTag = tagList.value[lastTagIndex]if (lastTag) {router.push(lastTag.fullPath)} else {router.push('/')}}onMounted(() => {document.addEventListener('click', state.closeMenu)})onBeforeUnmount(() => {document.removeEventListener('click', state.closeMenu)})return toRefs(state)
}

在src/pinia/modules下新建tags.js

import { defineStore } from 'pinia'
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
const TAGLIST = 'VEA-TAGLIST'export const useTags = defineStore('tags', {state: () => ({tagList: getItem(TAGLIST) || [],cacheList: [],activePosition: -1,}),actions: {saveActivePosition(index) {this.activePosition = index},addTag({ path, fullPath, name, meta, params, query }) {if (this.tagList.some(v => v.path === path)) return false// 添加tagListconst target = Object.assign({},{ path, fullPath, name, meta, params, query },{title: meta.title || '未命名',fullPath: fullPath || path,})if (this.activePosition === -1) {if (name === 'home') {this.tagList.unshift(target)} else {this.tagList.push(target)}} else {this.tagList.splice(this.activePosition + 1, 0, target)}// 保存到localStoragesetItem(TAGLIST, this.tagList)// 添加cacheListif (this.cacheList.includes(name)) returnif (!meta.noCache) {this.cacheList.push(name)}},deTagList(tag) {// 删除tagListthis.tagList = this.tagList.filter(v => v.path !== tag.path)// 保存到localStoragesetItem(TAGLIST, this.tagList)},deCacheList(tag) {// 删除cacheListthis.cacheList = this.cacheList.filter(v => v !== tag.name)},delTag(tag) {// 删除tagListthis.deTagList(tag)// 删除cacheListthis.deCacheList(tag)},delOtherTags(tag) {this.tagList = this.tagList.filter(v => !!v.meta.affix || v.path === tag.path)// 保存到localStoragesetItem(TAGLIST, this.tagList)this.cacheList = this.cacheList.filter(v => v === tag.name)},delSomeTags(tags) {this.tagList = this.tagList.filter(v => !!v.meta.affix || tags.every(tag => tag.path !== v.path))// 保存到localStoragesetItem(TAGLIST, this.tagList)this.cacheList = this.cacheList.filter(v =>tags.every(tag => tag.name !== v))},delAllTags() {this.tagList = this.tagList.filter(v => !!v.meta.affix)// 保存到localStorageremoveItem(TAGLIST)this.cacheList = []},updateTagList(tag) {const index = this.tagList.findIndex(v => v.path === tag.path)if (index > -1) {this.tagList[index] = Object.assign({}, this.tagList[index], tag)// 保存到localStoragesetItem(TAGLIST, this.tagList)}},clearAllTags() {this.cacheList = []this.tagList = []// 保存到localStorageremoveItem(TAGLIST)},},
})

src/layout/components/Content下新建index.vue,keep-alive组件会根据Component的name来跟include进行匹配,来缓存页面,同样的页面重新进入时不会触发onMounted,只会触发onActivated。

<template><router-view v-slot="{ Component }"><keep-alive :include="cacheList.join(',')"><component :is="Component" :key="key" /></keep-alive></router-view>
</template>
<script>
import { storeToRefs } from 'pinia'
import { computed, defineComponent } from 'vue'
import { useRoute } from 'vue-router'
import { useTags } from '@/pinia/modules/tags'export default defineComponent({setup() {const route = useRoute()const { cacheList } = storeToRefs(useTags())const key = computed(() => route.fullPath)return {cacheList,key,}},
})
</script>

实现同一个菜单多标签

框架通过vue-router来实现页面跳转和菜单展示,下面介绍对一个菜单,如果实现参数不同,显示多个tag。

按如下定义menu

{path: 'detail/:id',name: 'device_detail',component: () => import('@/views/device/detail.vue'),meta: { title: '设备详情', icon: 'el-icon-s-platform' },hidden: true,}

device/detail.vue中动态修改Component的name:
 

onMounted(() => {ctx.deviceId = parseInt(ctx.$route.params.id)ctx.$options.name = 'device_detail' + ctx.deviceId
})
onActivated(() => {ctx.$options.name = 'device_detail' + ctx.deviceId
})

修改src/pinia/modules/tags.js,修改地方:tag.name 改为 this.getFinalName(tag),即根据参数不同name也不同,name放入cacheList,用于唯一标识一个Component。

import { defineStore } from 'pinia'
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
const TAGLIST = 'VEA-TAGLIST'export const useTags = defineStore('tags', {state: () => ({tagList: getItem(TAGLIST) || [],cacheList: [],activePosition: -1,}),actions: {saveActivePosition(index) {this.activePosition = index},addTag({ path, fullPath, name, meta, params, query }) {if (this.tagList.some(v => v.path === path)) return falsevar title = meta.titleif (name == 'device_detail') {title = title + ' ' + query.name}// 添加tagListconst target = Object.assign({},{ path, fullPath, name, meta, params, query },{title: title || '未命名',fullPath: fullPath || path,})if (this.activePosition === -1) {if (name === 'home') {this.tagList.unshift(target)} else {this.tagList.push(target)}} else {this.tagList.splice(this.activePosition + 1, 0, target)}// 保存到localStoragesetItem(TAGLIST, this.tagList)// 添加cacheListconst finalName = this.getFinalName(target)if (this.cacheList.includes(finalName)) returnif (!meta.noCache) {this.cacheList.push(finalName)}},getFinalName(tag) {if (tag.name == 'device_detail') {return tag.name + tag.params.id}return tag.name},deTagList(tag) {// 删除tagListthis.tagList = this.tagList.filter(v => v.path !== tag.path)// 保存到localStoragesetItem(TAGLIST, this.tagList)},deCacheList(tag) {const name = this.getFinalName(tag)// 删除cacheListthis.cacheList = this.cacheList.filter(v => v !== name)},delTag(tag) {// 删除tagListthis.deTagList(tag)// 删除cacheListthis.deCacheList(tag)},delOtherTags(tag) {this.tagList = this.tagList.filter(v => !!v.meta.affix || v.path === tag.path)// 保存到localStoragesetItem(TAGLIST, this.tagList)const name = this.getFinalName(tag)this.cacheList = this.cacheList.filter(v => v === name)},delSomeTags(tags) {this.tagList = this.tagList.filter(v => !!v.meta.affix || tags.every(tag => tag.path !== v.path))// 保存到localStoragesetItem(TAGLIST, this.tagList)this.cacheList = this.cacheList.filter(v =>tags.every(tag => tag.name !== v))},delAllTags() {this.tagList = this.tagList.filter(v => !!v.meta.affix)// 保存到localStorageremoveItem(TAGLIST)this.cacheList = []},updateTagList(tag) {const index = this.tagList.findIndex(v => v.path === tag.path)if (index > -1) {this.tagList[index] = Object.assign({}, this.tagList[index], tag)// 保存到localStoragesetItem(TAGLIST, this.tagList)}},clearAllTags() {this.cacheList = []this.tagList = []// 保存到localStorageremoveItem(TAGLIST)},},
})

device/detail/:id,不同参数时页面缓存删不掉的问题

现象如下:进入/device/detail/1,再打开/device/detail/2,点击其他标签,删掉/device/detail/2标签,再打开/device/detail/2,此时发现只触发了onActivated方法,没有触发onMounted方法,页面没有重新渲染,keepalive这里的缓存机制不清楚,但是可以知道框架误以为/device/detail/2还在缓存中,直接把缓存中的页面拿过来显示了。

解决方法
对于这种动态菜单的情况,Compnent的key属性增加自增的标识,每次打开标识加1。
修改src/pinia/modules/tags.js,增加detailIndex,在addTag时增加detailIndex的修改
 

import { defineStore } from 'pinia'
import { getItem, setItem, removeItem } from '@/utils/storage' //getItem和setItem是封装的操作localStorage的方法
const TAGLIST = 'VEA-TAGLIST'export const useTags = defineStore('tags', {state: () => ({tagList: getItem(TAGLIST) || [],cacheList: [],activePosition: -1,detailIndex: {}}),actions: {saveActivePosition(index) {this.activePosition = index},addTag({ path, fullPath, name, meta, params, query }) {if (this.tagList.some(v => v.path === path)) return falsevar title = meta.titleif (name == 'device_detail') {title = title + ' ' + query.name}// 添加tagListconst target = Object.assign({},{ path, fullPath, name, meta, params, query },{title: title || '未命名',fullPath: fullPath || path,})if (this.activePosition === -1) {if (name === 'home') {this.tagList.unshift(target)} else {this.tagList.push(target)}} else {this.tagList.splice(this.activePosition + 1, 0, target)}// 保存到localStoragesetItem(TAGLIST, this.tagList)// 添加cacheListconst finalName = this.getFinalName(target)if (this.cacheList.includes(finalName)) returnif (!meta.noCache) {if (finalName.startsWith('device_detail')) {if (!this.detailIndex[target.path]) {this.detailIndex[target.path] = 1} else {this.detailIndex[target.path]++}} else {this.detailIndex[target.path] = ''}this.cacheList.push(finalName)}},

修改src/layout/components/Content/index.vue中的key未route.path + detailIndex.value[route.path]

<template><router-view v-slot="{ Component }"><keep-alive :include="cacheList.join(',')"><component :is="Component" :key="key" /></keep-alive></router-view>
</template>
<script>
import { storeToRefs } from 'pinia'
import { computed, defineComponent } from 'vue'
import { useRoute } from 'vue-router'
import { useTags } from '@/pinia/modules/tags'export default defineComponent({setup() {const route = useRoute()const { cacheList, detailIndex } = storeToRefs(useTags())const key = computed(() => route.path + detailIndex[route.path])return {cacheList,key,}},
})
</script>

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

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

相关文章

生产车间图纸无纸化,生产车间图纸无纸化解决方案

生产车间图纸无纸化是指通过采用数字化设备和技术&#xff0c;将传统的纸质图纸转化为电子文档&#xff0c;并在生产过程中实现图纸的电子化、网络化和自动化管理。这一转变旨在提高工作效率、降低成本、提高安全性&#xff0c;并推动生产车间的现代化和智能化。 实现生产车间图…

Java中的锁机制,包括可重入锁、读写锁等。

Java中的锁机制是一种用于控制多个线程对共享资源的访问&#xff0c;以避免数据不一致和其他并发问题的技术。Java提供了多种锁机制&#xff0c;每种都有其特定的用途和优势。 1. 内置锁&#xff08;synchronized&#xff09; Java语言本身提供了内置锁机制&#xff0c;通过s…

蓝桥杯算法题:最大比例

题目描述&#xff1a; X星球的某个大奖赛设了 M 级奖励。 每个级别的奖金是一个正整数。 并且&#xff0c;相邻的两个级别间的比例是个固定值。 也就是说&#xff1a;所有级别的奖金数构成了一个等比数列。 比如&#xff1a;16,24,36,54&#xff0c;其等比值为&#xff1a;3/2。…

SpringBoot 定时任务实践、定时任务按指定时间执行

Q1. springboot怎样创建定时任务&#xff1f; 很显然&#xff0c;人人都知道&#xff0c;Scheduled(cron ".....") Q2. 如上所示创建了定时任务却未能执行是为什么&#xff1f; 如果你的cron确定没写错的话 cron表达式是否合法&#xff0c;可参考此处&#xff0c…

Android集成微信支付

官方api&#xff1a;Android接入指南 | 微信开放文档 打开微信开放平台登录账户后点击创建应用 根据提示填写完相应的内容提交审核通过后&#xff0c;需要开通支付权限。 1.接着在你的项目工程build.gradle文件中添加微信支付依赖库 ​api com.tencent.mm.opensdk:wechat-sdk…

python代码使用过程中使用快捷键注释时报错

1.代码 2.代码报错 3.代码注释后的结果 4. 原因

sql注入方式之联合注入

1.1 靶场环境 系统centos7 IP地址192.168.1.24 1.2 联合注入原理 联合查询注入是联合两个表进行注入攻击&#xff0c;使用关键词 union select 对两个表进行联合查询。两个表的字段要数要相同&#xff0c;不然会出现报错。 1.3 找注入点 找注入点&#xff0c;当输入id1 an…

C语言——文件管理

文件&#xff1a;即磁盘上的文件&#xff0c;使用文件可以将数据直接存放在电脑的硬盘上&#xff0c;做到数据持久化。 在程序设计中&#xff0c;按文件的功能划分&#xff0c;将文件分为程序文件与数据文件 程序文件 程序文件包括源文件&#xff08;.c&#xff09;&#xff0…

Centos7搭建 Skywalking 单机版

介绍 Skywalking是应用性能监控平台&#xff0c;可用于分布式系统&#xff0c;支持微服务、云原生、Docker、Kubernetes 等多种架构场景。 整体架构如图 Agent &#xff1a;在应用中&#xff0c;收集 Trace、Log、Metrics 等监控数据&#xff0c;使用 RPC、RESTful API、Kafk…

【算法详解】二分查找

1. 二分查找算法介绍 「二分查找算法&#xff08;Binary Search Algorithm&#xff09;」&#xff1a;也叫做 「折半查找算法」、「对数查找算法」。是一种在有序数组中查找某一特定元素的搜索算法。 基本算法思想&#xff1a;先确定待查找元素所在的区间范围&#xff0c;在逐步…

骨传导耳机哪个品牌质量比较好?精选六大必看的耳机选购指南

骨传导耳机&#xff0c;是作为近年来深受消费者喜欢的蓝牙耳机产品&#xff0c;目前已经成为了热度最高的骨蓝牙耳机。然而&#xff0c;随着骨传导耳机的热度增高&#xff0c;市场上开始出现一些质量参差不齐、音质不佳的劣质产品&#xff0c;如果使用这些劣质产品的话&#xf…

C语言从入门到实战————文件操作

目录 前言 1. 为什么使用文件&#xff1f; 2. 什么是文件&#xff1f; 2.1 程序文件 2.2 数据文件 2.3 文件名 3. ⼆进制文件和文本文件&#xff1f; 4. 文件的打开和关闭 4.1 流和标准流 4.1.1 流 4.1.2 标准流 4.2 文件指针 4.3 文件的打开和关闭 5. 文…

SVG使用记录

<g>标签用于将多个形状组成一个组&#xff08;group&#xff09;&#xff0c;方便复用。 <defs>标签用于自定义形状&#xff0c;它内部的代码不会显示&#xff0c;仅供引用。 view0SVG.select(.svg-pan-zoom_viewport).get(0); view0.c…

nuxt3 使用$fetch封装(typescript)客户端使用的http请求方法

nuxt3提供了usefetch()之类的方法来发起请求&#xff0c;他提供的这些方法貌似都是考虑和server端配合使用的&#xff0c;比如说在页面初始化前&#xff0c;有些数据是由后端提供&#xff0c;但是为了按SSR来渲染页面&#xff0c;就可以使用usefetch()来配合server先从后端获取…

基于java+springboot+vue实现的旅游管理系统(文末源码+Lw)23-234

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统旅游管理系统信息管理难度大&#xff0c;容错率低&#…

使用yolov8实现自动车牌识别(教程+代码)

该项目利用了一个被标记为“YOLOv8”的目标检测模型&#xff0c;专门针对车牌识别任务进行训练和优化。整个系统通常分为以下几个核心步骤&#xff1a; 数据准备&#xff1a; 收集包含车牌的大量图片&#xff0c;并精确地标记车牌的位置和文本信息。数据集可能包含各种环境下的…

短剧小程序开发:解锁碎片时间的观影新风尚

随着移动互联网的飞速发展&#xff0c;人们对内容的消费方式也在不断升级。短剧作为一种新型的内容形态&#xff0c;正逐渐受到广大用户的青睐。为了满足用户随时随地观看短剧的需求&#xff0c;我们推出了全新的短剧小程序开发服务&#xff0c;为您解锁碎片时间的观影新风尚。…

C/C++中局部变量static用法实例

1. 普通局部变量存储于进程栈空间&#xff0c;使用完毕会立即释放&#xff0c;静态局部变量使用static修饰符定义&#xff0c;即使在声明时未赋初值&#xff0c;编译器也会把它初始化为0&#xff0c;并且静态局部变量存储于进程的全局数据区&#xff0c;即使函数返回&#xff0…

解密项目管理专业术语:十大名词背后的实战技巧

项目管理是一门综合学科&#xff0c;涵盖了一系列方法、技能和工具。今天为大家带来项目管理的十大专业术语&#xff0c;它们分别是项目范围、利益相关者管理、工作分解结构&#xff08;WBS&#xff09;、里程碑、风险管理、资源分配、关键路径法&#xff08;CPM&#xff09;、…

双向链表介绍

目录 1. 双向链表的结构 2. 双向链表的实现 初始化哨兵位&#xff1a; 打印链表&#xff1a; 尾插&#xff1a; 头插&#xff1a; 尾删&#xff1a; 头删&#xff1a; 查找&#xff1a; 在指定位置之后插入数据&#xff1a; 删除目标位置的数据&#xff1a; 销毁链…