影视网站建设需要学什么,非法网站开发,电商首页设计思路,全国企业信息信用公示系统原框架代码: 赵志江/huzhushan-vue3-element-admin
目录
TagsBar实现
实现同一个菜单多标签
device/detail/:id#xff0c;不同参数时页面缓存删不掉的问题 TagsBar实现
在src/layout/components/下新建目录Tagsbar#xff0c;新建index.vue
templatediv c…原框架代码: 赵志江/huzhushan-vue3-element-admin
目录
TagsBar实现
实现同一个菜单多标签
device/detail/:id不同参数时页面缓存删不掉的问题 TagsBar实现
在src/layout/components/下新建目录Tagsbar新建index.vue
templatediv classtags-container :class{ hide: !isTagsbarShow }el-scrollbarrefscrollContainer:verticalfalseclassscroll-containerwheel.preventonScrollrouter-linkv-for(tag, i) in tagList:keytag.fullPath:totag:refel setItemRef(i, el)customv-slot{ navigate, isExactActive }divclasstags-item:classisExactActive? active : clicknavigateclick.middlecloseTag(tag)contextmenu.preventopenMenu(tag, $event)span classtitle{{ $t(tag.title) }}/spanel-iconv-if!isAffix(tag)classel-icon-closeclick.prevent.stopcloseTag(tag)Close //el-icon/div/router-link/el-scrollbar/divulv-showvisible:style{ left: left px, top: top px }classcontextmenu!-- li clickrefreshSelectedTag(selectedTag){{ $t(tags.refresh) }}/li --li v-if!isAffix(selectedTag) clickcloseTag(selectedTag){{ $t(tags.close) }}/lili clickcloseOtherTags{{ $t(tags.other) }}/lili clickcloseLeftTags{{ $t(tags.left) }}/lili clickcloseRightTags{{ $t(tags.right) }}/lili clickcloseAllTags{{ $t(tags.all) }}/li/ul
/templatescript
import { defineComponent, computed, getCurrentInstance } from vue
import { useTags } from ./hooks/useTags
import { useContextMenu } from ./hooks/useContextMenu
import { useLayoutsettings } from /pinia/modules/layoutSettingsexport 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}},
})
/scriptstyle langscss 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-routerexport 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 vueexport 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 ./useTagsexport 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-TAGLISTexport 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。
templaterouter-view v-slot{ Component }keep-alive :includecacheList.join(,)component :isComponent :keykey //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/tagsexport 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-TAGLISTexport 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-TAGLISTexport 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]
templaterouter-view v-slot{ Component }keep-alive :includecacheList.join(,)component :isComponent :keykey //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/tagsexport default defineComponent({setup() {const route useRoute()const { cacheList, detailIndex } storeToRefs(useTags())const key computed(() route.path detailIndex[route.path])return {cacheList,key,}},
})
/script