首页 分享 TagsView.vue 有效果图

TagsView.vue 有效果图

来源:花匠小妙招 时间:2024-12-13 14:44

先上代码,项目结构图和效果图在后面展示。

1.TagsView.vue

<template>

<div class="tags-view-container">

<scroll-pane class="tags-view-wrapper" ref="scrollPane">

<router-link ref="tag" class="isActive(tag)?'active':''" :to="tag" @contextmenu.prevent.native="openMenu(tag,$event)" v-for="tag in Array.from(visitedViews)" :key="tag.path" >

{{tag.title}}

<span class="el-icon-close" @click.prevent.stop='closeSelectedTag(tag)'></span>

</router-link>

</scroll-pane>

<ul class='contextmenu' v-show="visible" :style="{left:left+'px',top:top+'px'}">

<li @click="closeSelectedTag(selectedTag)">关闭</li>

<li @click="closeOthersTags">关闭其他</li>

<li @click="closeAllTags">关闭所有</li>

</ul>

</div>

</template>

<script>

import ScrollPane from '@/components/ScrollPane'

export default {

name: "tags-view",

components: { ScrollPane },

data(){

return{

visible: false,

top: 0,

left: 0,

selectedTag: {}

}

},

computed:{

visitedViews(){

console.log('tabView')

console.log(this.$store.state.tagsView)

console.log(this.$store.state)

return this.$store.state.tagsView.visitedViews

}

},

watch:{

$route(){

this.addViewTags()

this.moveToCurrentTag()

},

visible(value) {

if (value) {

document.body.addEventListener('click', this.closeMenu)

} else {

document.body.removeEventListener('click', this.closeMenu)

}

}

},

mounted() {

this.addViewTags()

},

methods:{

generateRoute(){

if (this.$route.name) {

return this.$route

}

return false

},

isActive(route) {

return route.path === this.$route.path

},

addViewTags() {

const route = this.generateRoute()

if (!route) {

return false

}

this.$store.dispatch('addVisitedViews', route)

},

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.$el)

break

}

}

})

},

closeSelectedTag(view) {

this.$store.dispatch('delVisitedViews', view).then((views) => {

if (this.isActive(view)) {

const latestView = views.slice(-1)[0]

if (latestView) {

this.$router.push(latestView)

} else {

this.$router.push('/')

}

}

})

},

closeOthersTags() {

this.$router.push(this.selectedTag)

this.$store.dispatch('delOthersViews', this.selectedTag).then(() => {

this.moveToCurrentTag()

})

},

closeAllTags() {

this.$store.dispatch('delAllViews')

this.$router.push('/')

},

openMenu(tag, e) {

this.visible = true

this.selectedTag = tag

const offsetLeft = this.$el.getBoundingClientRect().left // container margin left

this.left = e.clientX - offsetLeft + 15 // 15: margin right

this.top = e.clientY

},

closeMenu() {

this.visible = false

}

}

}

</script>

<style rel="stylesheet/scss" lang="scss" scoped>

.tags-view-container {

.tags-view-wrapper {

background: #fff;

height: 34px;

border-bottom: 1px solid #d8dce5;

box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);

.tags-view-item {

display: inline-block;

position: relative;

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;

}

&.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: 100;

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, .3);

li {

margin: 0;

padding: 7px 16px;

cursor: pointer;

&:hover {

background: #eee;

}

}

}

}

</style>

<style rel="stylesheet/scss" 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 .3s cubic-bezier(.645, .045, .355, 1);

transform-origin: 100% 50%;

&:before {

transform: scale(.6);

display: inline-block;

vertical-align: -3px;

}

&:hover {

background-color: #b4bccc;

color: #fff;

}

}

}

}

</style>

2.ScrollPane.vue:是当tagView内容超出一行时候的滚动(将鼠标悬浮在那一行上,不出现滚动条,该行就可以滚动)

<template>

<div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll">

<div class="scroll-wrapper" ref="scrollWrapper" :style="{left: left + 'px'}">

<slot></slot>

</div>

</div>

</template>

<script>

const padding = 15

export default {

name: 'scrollPane',

data() {

return {

left: 0

}

},

methods: {

handleScroll(e) {

const eventDelta = e.wheelDelta || -e.deltaY * 3

const $container = this.$refs.scrollContainer

const $containerWidth = $container.offsetWidth

const $wrapper = this.$refs.scrollWrapper

const $wrapperWidth = $wrapper.offsetWidth

if (eventDelta > 0) {

this.left = Math.min(0, this.left + eventDelta)

} else {

if ($containerWidth - padding < $wrapperWidth) {

if (this.left < -($wrapperWidth - $containerWidth + padding)) {

this.left = this.left

} else {

this.left = Math.max(this.left + eventDelta, $containerWidth - $wrapperWidth - padding)

}

} else {

this.left = 0

}

}

},

moveToTarget($target) {

const $container = this.$refs.scrollContainer

const $containerWidth = $container.offsetWidth

const $targetLeft = $target.offsetLeft

const $targetWidth = $target.offsetWidth

if ($targetLeft < -this.left) {

this.left = -$targetLeft + padding

} else if ($targetLeft + padding > -this.left && $targetLeft + $targetWidth < -this.left + $containerWidth - padding) {

} else {

this.left = -($targetLeft - ($containerWidth - $targetWidth) + padding)

}

}

}

}

</script>

<style rel="stylesheet/scss" lang="scss" scoped>

.scroll-container {

white-space: nowrap;

position: relative;

overflow: hidden;

width: 100%;

.scroll-wrapper {

position: absolute;

}

}

</style>

3.Store/tagsView.js

const tagsView = {

state: {

visitedViews: [],

cachedViews: []

},

mutations: {

ADD_VISITED_VIEWS: (state, view) => {

if (state.visitedViews.some(v => v.path === view.path)) return

state.visitedViews.push(Object.assign({}, view, {

title: view.meta.title || 'no-name'

}))

if (!view.meta.noCache) {

state.cachedViews.push(view.name)

}

},

DEL_VISITED_VIEWS: (state, view) => {

for (const [i, v] of state.visitedViews.entries()) {

if (v.path === view.path) {

state.visitedViews.splice(i, 1)

break

}

}

for (const i of state.cachedViews) {

if (i === view.name) {

const index = state.cachedViews.indexOf(i)

state.cachedViews.splice(index, 1)

break

}

}

},

DEL_OTHERS_VIEWS: (state, view) => {

for (const [i, v] of state.visitedViews.entries()) {

if (v.path === view.path) {

state.visitedViews = state.visitedViews.slice(i, i + 1)

break

}

}

for (const i of state.cachedViews) {

if (i === view.name) {

const index = state.cachedViews.indexOf(i)

state.cachedViews = state.cachedViews.slice(index, i + 1)

break

}

}

},

DEL_ALL_VIEWS: (state) => {

state.visitedViews = []

state.cachedViews = []

}

},

actions: {

addVisitedViews({ commit }, view) {

commit('ADD_VISITED_VIEWS', view)

},

delVisitedViews({ commit, state }, view) {

return new Promise((resolve) => {

commit('DEL_VISITED_VIEWS', view)

resolve([...state.visitedViews])

})

},

delOthersViews({ commit, state }, view) {

return new Promise((resolve) => {

commit('DEL_OTHERS_VIEWS', view)

resolve([...state.visitedViews])

})

},

delAllViews({ commit, state }) {

return new Promise((resolve) => {

commit('DEL_ALL_VIEWS')

resolve([...state.visitedViews])

})

}

}

}

export default tagsView

4.将tagsView.js引入到store/index.js中

import Vue from 'vue'

import Vuex from 'vuex'

import user from './modules/user'

import app from './modules/app'

import permission from './modules/permission'

import tagsView from './modules/tagsView'

import getters from './getters'

Vue.use(Vuex)

const store= new Vuex.Store({

modules:{

user,

app,

permission,

tagsView

},

getters

})

export default store

总体功能分析:

1.选择左侧菜单的时候,通过监听路由变化,将左侧菜单的选中项缓存到store中,并显示在tagsView中

2.有右侧菜单事件:textmenu

3.点击名字时候,跳转到相应页面

4.点击关闭时候,关闭当前选项卡

项目结构:

别忘了再路由上面加name属性,楼主因为这个调试了好久

效果图:

相关知识

效果图婚礼效果图
花坛效果图效果图免费下载
特制商场花车效果图定制商场花车效果图
花店装修风格有哪些 花店装修风格效果图
花园风格效果图 16张田园风格露天花园设计效果图
小景效果图
鲜花店装饰效果图
草坪婚礼效果图婚礼效果图
鲜花店装修风格有几种?15平米小型花店装修效果图分享
春节花卉摆放效果图,办公楼花卉摆放效果图

网址: TagsView.vue 有效果图 https://www.huajiangbk.com/newsview1076383.html

所属分类:花卉
上一篇: 带你认识特应性皮炎
下一篇: assert 断言

推荐分享