feat:vue-element-admin升级vue3
This commit is contained in:
parent
fe8a7e2c31
commit
0ec8710e6f
|
|
@ -1,175 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-color-picker
|
|
||||||
v-model="theme"
|
|
||||||
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
|
|
||||||
class="theme-picker"
|
|
||||||
popper-class="theme-picker-dropdown"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const version = require('element-ui/package.json').version // element-ui version from node_modules
|
|
||||||
const ORIGINAL_THEME = '#409EFF' // default color
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chalk: '', // content of theme-chalk css
|
|
||||||
theme: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
defaultTheme() {
|
|
||||||
return this.$store.state.settings.theme
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
defaultTheme: {
|
|
||||||
handler: function(val, oldVal) {
|
|
||||||
this.theme = val
|
|
||||||
},
|
|
||||||
immediate: true
|
|
||||||
},
|
|
||||||
async theme(val) {
|
|
||||||
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
|
|
||||||
if (typeof val !== 'string') return
|
|
||||||
const themeCluster = this.getThemeCluster(val.replace('#', ''))
|
|
||||||
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
|
|
||||||
console.log(themeCluster, originalCluster)
|
|
||||||
|
|
||||||
const $message = this.$message({
|
|
||||||
message: ' Compiling the theme',
|
|
||||||
customClass: 'theme-message',
|
|
||||||
type: 'success',
|
|
||||||
duration: 0,
|
|
||||||
iconClass: 'el-icon-loading'
|
|
||||||
})
|
|
||||||
|
|
||||||
const getHandler = (variable, id) => {
|
|
||||||
return () => {
|
|
||||||
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
|
|
||||||
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
|
|
||||||
|
|
||||||
let styleTag = document.getElementById(id)
|
|
||||||
if (!styleTag) {
|
|
||||||
styleTag = document.createElement('style')
|
|
||||||
styleTag.setAttribute('id', id)
|
|
||||||
document.head.appendChild(styleTag)
|
|
||||||
}
|
|
||||||
styleTag.innerText = newStyle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.chalk) {
|
|
||||||
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
|
|
||||||
await this.getCSSString(url, 'chalk')
|
|
||||||
}
|
|
||||||
|
|
||||||
const chalkHandler = getHandler('chalk', 'chalk-style')
|
|
||||||
|
|
||||||
chalkHandler()
|
|
||||||
|
|
||||||
const styles = [].slice.call(document.querySelectorAll('style'))
|
|
||||||
.filter(style => {
|
|
||||||
const text = style.innerText
|
|
||||||
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
|
|
||||||
})
|
|
||||||
styles.forEach(style => {
|
|
||||||
const { innerText } = style
|
|
||||||
if (typeof innerText !== 'string') return
|
|
||||||
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.$emit('change', val)
|
|
||||||
|
|
||||||
$message.close()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
updateStyle(style, oldCluster, newCluster) {
|
|
||||||
let newStyle = style
|
|
||||||
oldCluster.forEach((color, index) => {
|
|
||||||
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
|
|
||||||
})
|
|
||||||
return newStyle
|
|
||||||
},
|
|
||||||
|
|
||||||
getCSSString(url, variable) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const xhr = new XMLHttpRequest()
|
|
||||||
xhr.onreadystatechange = () => {
|
|
||||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
|
||||||
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhr.open('GET', url)
|
|
||||||
xhr.send()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
getThemeCluster(theme) {
|
|
||||||
const tintColor = (color, tint) => {
|
|
||||||
let red = parseInt(color.slice(0, 2), 16)
|
|
||||||
let green = parseInt(color.slice(2, 4), 16)
|
|
||||||
let blue = parseInt(color.slice(4, 6), 16)
|
|
||||||
|
|
||||||
if (tint === 0) { // when primary color is in its rgb space
|
|
||||||
return [red, green, blue].join(',')
|
|
||||||
} else {
|
|
||||||
red += Math.round(tint * (255 - red))
|
|
||||||
green += Math.round(tint * (255 - green))
|
|
||||||
blue += Math.round(tint * (255 - blue))
|
|
||||||
|
|
||||||
red = red.toString(16)
|
|
||||||
green = green.toString(16)
|
|
||||||
blue = blue.toString(16)
|
|
||||||
|
|
||||||
return `#${red}${green}${blue}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const shadeColor = (color, shade) => {
|
|
||||||
let red = parseInt(color.slice(0, 2), 16)
|
|
||||||
let green = parseInt(color.slice(2, 4), 16)
|
|
||||||
let blue = parseInt(color.slice(4, 6), 16)
|
|
||||||
|
|
||||||
red = Math.round((1 - shade) * red)
|
|
||||||
green = Math.round((1 - shade) * green)
|
|
||||||
blue = Math.round((1 - shade) * blue)
|
|
||||||
|
|
||||||
red = red.toString(16)
|
|
||||||
green = green.toString(16)
|
|
||||||
blue = blue.toString(16)
|
|
||||||
|
|
||||||
return `#${red}${green}${blue}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const clusters = [theme]
|
|
||||||
for (let i = 0; i <= 9; i++) {
|
|
||||||
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
|
|
||||||
}
|
|
||||||
clusters.push(shadeColor(theme, 0.1))
|
|
||||||
return clusters
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.theme-message,
|
|
||||||
.theme-picker-dropdown {
|
|
||||||
z-index: 99999 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-picker .el-color-picker__trigger {
|
|
||||||
height: 26px !important;
|
|
||||||
width: 26px !important;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-picker-dropdown .el-color-dropdown__link-btn {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,94 +1,99 @@
|
||||||
<template>
|
<template>
|
||||||
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
|
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
|
||||||
<slot />
|
<slot/>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
const tagAndTagSpacing = 4 // tagAndTagSpacing
|
import {defineComponent, reactive, ref, toRefs, computed, onMounted, onBeforeUnmount, getCurrentInstance} from "vue";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: 'ScrollPane',
|
emits: ['scroll'],
|
||||||
data() {
|
setup(_, context) {
|
||||||
return {
|
const scrollContainer = ref(null)
|
||||||
left: 0
|
const scrollWrapper = computed(() => {
|
||||||
}
|
return (scrollContainer.value as any).$refs.wrap as HTMLElement
|
||||||
},
|
})
|
||||||
computed: {
|
const {ctx} = getCurrentInstance() as any
|
||||||
scrollWrapper() {
|
const tagAndTagSpacing = 4
|
||||||
return this.$refs.scrollContainer.$refs.wrap
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
this.scrollWrapper.removeEventListener('scroll', this.emitScroll)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleScroll(e) {
|
|
||||||
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
|
||||||
const $scrollWrapper = this.scrollWrapper
|
|
||||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
|
||||||
},
|
|
||||||
emitScroll() {
|
|
||||||
this.$emit('scroll')
|
|
||||||
},
|
|
||||||
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
|
const state = reactive({
|
||||||
let lastTag = null
|
handleScroll: (e: WheelEvent) => {
|
||||||
|
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40
|
||||||
|
scrollWrapper.value.scrollLeft = scrollWrapper.value.scrollLeft + eventDelta / 4
|
||||||
|
},
|
||||||
|
moveToCurrentTag: (currentTag: HTMLElement) => {
|
||||||
|
const container = (scrollContainer.value as any).$el as HTMLElement
|
||||||
|
const containerWidth = container.offsetWidth
|
||||||
|
const tagList = ctx.$parent.$refs.tag as any[]
|
||||||
|
let firstTag = null
|
||||||
|
let lastTag = null
|
||||||
|
|
||||||
// find first tag and last tag
|
// find first tag and last tag
|
||||||
if (tagList.length > 0) {
|
if (tagList.length > 0) {
|
||||||
firstTag = tagList[0]
|
firstTag = tagList[0]
|
||||||
lastTag = tagList[tagList.length - 1]
|
lastTag = tagList[tagList.length - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstTag === currentTag) {
|
if (firstTag === currentTag) {
|
||||||
$scrollWrapper.scrollLeft = 0
|
scrollWrapper.value.scrollLeft = 0
|
||||||
} else if (lastTag === currentTag) {
|
} else if (lastTag === currentTag) {
|
||||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
scrollWrapper.value.scrollLeft = scrollWrapper.value.scrollWidth - containerWidth
|
||||||
} else {
|
} else {
|
||||||
// find preTag and nextTag
|
// find preTag and nextTag
|
||||||
const currentIndex = tagList.findIndex(item => item === currentTag)
|
const currentIndex = tagList.findIndex(item => item === currentTag)
|
||||||
const prevTag = tagList[currentIndex - 1]
|
const prevTag = tagList[currentIndex - 1]
|
||||||
const nextTag = 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
|
||||||
|
|
||||||
// the tag's offsetLeft after of nextTag
|
if (afterNextTagOffsetLeft > scrollWrapper.value.scrollLeft + containerWidth) {
|
||||||
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
|
scrollWrapper.value.scrollLeft = afterNextTagOffsetLeft - containerWidth
|
||||||
|
} else if (beforePrevTagOffsetLeft < scrollWrapper.value.scrollLeft) {
|
||||||
// the tag's offsetLeft before of prevTag
|
scrollWrapper.value.scrollLeft = beforePrevTagOffsetLeft
|
||||||
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
|
}
|
||||||
|
|
||||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
|
||||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
|
||||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
|
||||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emitScroll = () => {
|
||||||
|
context.emit('scroll')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
scrollWrapper.value.addEventListener('scroll', emitScroll, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
scrollWrapper.value.removeEventListener('scroll', emitScroll)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
scrollContainer,
|
||||||
|
...toRefs(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.scroll-container {
|
||||||
|
.el-scrollbar__bar {
|
||||||
|
bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-scrollbar__wrap {
|
||||||
|
height: 49px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.scroll-container {
|
.scroll-container {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
::v-deep {
|
|
||||||
.el-scrollbar__bar {
|
|
||||||
bottom: 0px;
|
|
||||||
}
|
|
||||||
.el-scrollbar__wrap {
|
|
||||||
height: 49px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -2,80 +2,142 @@
|
||||||
<div id="tags-view-container" class="tags-view-container">
|
<div id="tags-view-container" class="tags-view-container">
|
||||||
<scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
|
<scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
|
||||||
<router-link
|
<router-link
|
||||||
v-for="tag in visitedViews"
|
v-for="tag in visitedViews"
|
||||||
ref="tag"
|
ref="tag"
|
||||||
:key="tag.path"
|
:key="tag.path"
|
||||||
:class="isActive(tag)?'active':''"
|
:class="isActive(tag)?'active':''"
|
||||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||||
tag="span"
|
tag="span"
|
||||||
class="tags-view-item"
|
class="tags-view-item"
|
||||||
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
|
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
|
||||||
@contextmenu.prevent.native="openMenu(tag,$event)"
|
@contextmenu.prevent.native="openMenu(tag,$event)"
|
||||||
>
|
>
|
||||||
{{ generateTitle(tag.title) }}
|
{{ tag.meta.title }}
|
||||||
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
|
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</scroll-pane>
|
</scroll-pane>
|
||||||
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
|
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
|
||||||
<li @click="refreshSelectedTag(selectedTag)">{{ $t('tagsView.refresh') }}</li>
|
<li @click="refreshSelectedTag(selectedTag)">刷新</li>
|
||||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">{{ $t('tagsView.close') }}</li>
|
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭</li>
|
||||||
<li @click="closeOthersTags">{{ $t('tagsView.closeOthers') }}</li>
|
<li @click="closeOthersTags">关闭其它</li>
|
||||||
<li @click="closeAllTags(selectedTag)">{{ $t('tagsView.closeAll') }}</li>
|
<li @click="closeAllTags(selectedTag)">关闭所有</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import ScrollPane from './ScrollPane'
|
import ScrollPane from './ScrollPane.vue'
|
||||||
import { generateTitle } from '@/utils/i18n'
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import {useStore} from "@store";
|
||||||
|
import {
|
||||||
|
defineComponent,
|
||||||
|
computed,
|
||||||
|
getCurrentInstance,
|
||||||
|
nextTick,
|
||||||
|
onBeforeMount,
|
||||||
|
reactive,
|
||||||
|
ref,
|
||||||
|
toRefs,
|
||||||
|
watch
|
||||||
|
} from "vue";
|
||||||
|
import {RouteRecordRaw, useRoute, useRouter} from 'vue-router'
|
||||||
|
import {TagView} from "@store/interface";
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
components: { ScrollPane },
|
components: {ScrollPane},
|
||||||
data() {
|
setup() {
|
||||||
return {
|
const store = useStore()
|
||||||
|
const router = useRouter()
|
||||||
|
const instance = getCurrentInstance()
|
||||||
|
const currentRoute = useRoute()
|
||||||
|
const scrollPaneRef = ref(null)
|
||||||
|
const {ctx} = instance
|
||||||
|
|
||||||
|
const toLastView=(visitedViews,view)=>{
|
||||||
|
const latestView = visitedViews.slice(-1)[0]
|
||||||
|
if (latestView && latestView.fullPath) {
|
||||||
|
router.push(latestView.fullPath)
|
||||||
|
} else {
|
||||||
|
if (view.name === 'Dashboard') {
|
||||||
|
router.push({path: '/redirect' + view.fullPath})
|
||||||
|
} else {
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const state =reactive({
|
||||||
visible: false,
|
visible: false,
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
selectedTag: {},
|
selectedTag: {},
|
||||||
affixTags: []
|
affixTags: [],
|
||||||
}
|
isActive: (route) => {
|
||||||
},
|
return route.path === currentRoute.path
|
||||||
computed: {
|
},
|
||||||
visitedViews() {
|
isAffix: (tag) => {
|
||||||
return this.$store.state.tagsView.visitedViews
|
return tag.meta && tag.meta.affix
|
||||||
},
|
},
|
||||||
routes() {
|
refreshSelectedTag: (view: TagView) => {
|
||||||
return this.$store.state.permission.routes
|
store.dispatch('tagsView/delCachedView', view)
|
||||||
}
|
const { fullPath } = view
|
||||||
},
|
nextTick(() => {
|
||||||
watch: {
|
router.replace({ path: '/redirect' + fullPath }).catch(err => {
|
||||||
$route() {
|
console.warn(err)
|
||||||
this.addTags()
|
})
|
||||||
this.moveToCurrentTag()
|
})
|
||||||
},
|
},
|
||||||
visible(value) {
|
closeSelectedTag: (view: TagView) => {
|
||||||
if (value) {
|
store.dispatch('tagsView/delView', view)
|
||||||
document.body.addEventListener('click', this.closeMenu)
|
if (state.isActive(view)) {
|
||||||
} else {
|
toLastView(store.state.tagsView.visitedViews, view)
|
||||||
document.body.removeEventListener('click', this.closeMenu)
|
}
|
||||||
|
},
|
||||||
|
closeOthersTags: () => {
|
||||||
|
if (state.selectedTag.fullPath !== currentRoute.path && state.selectedTag.fullPath !== undefined) {
|
||||||
|
router.push(state.selectedTag.fullPath)
|
||||||
|
}
|
||||||
|
store.dispatch('tagsView/delOthersViews', state.selectedTag as TagView)
|
||||||
|
},
|
||||||
|
closeAllTags: (view: TagView) => {
|
||||||
|
store.dispatch('tagsView/delAllViews', undefined)
|
||||||
|
if (state.affixTags.some(tag => tag.path === currentRoute.path)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
toLastView(store.state.tagsView.visitedViews, view)
|
||||||
|
},
|
||||||
|
openMenu: (tag: TagView, e: MouseEvent) => {
|
||||||
|
const menuMinWidth = 105
|
||||||
|
const offsetLeft = ctx.$el.getBoundingClientRect().left // container margin left
|
||||||
|
const offsetWidth = ctx.$el.offsetWidth // container width
|
||||||
|
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||||
|
const left = e.clientX - offsetLeft + 15 // 15: margin right
|
||||||
|
if (left > maxLeft) {
|
||||||
|
state.left = maxLeft
|
||||||
|
} else {
|
||||||
|
state.left = left
|
||||||
|
}
|
||||||
|
state.top = e.clientY
|
||||||
|
state.visible = true
|
||||||
|
state.selectedTag = tag
|
||||||
|
},
|
||||||
|
closeMenu: () => {
|
||||||
|
state.visible = false
|
||||||
|
},
|
||||||
|
handleScroll: () => {
|
||||||
|
state.closeMenu()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
})
|
||||||
mounted() {
|
|
||||||
this.initTags()
|
const visitedViews = computed(() => {
|
||||||
this.addTags()
|
return store.state.tagsView.visitedViews
|
||||||
},
|
})
|
||||||
methods: {
|
const routes = computed(() => store.state.permission.routes)
|
||||||
generateTitle, // generateTitle by vue-i18n
|
|
||||||
isActive(route) {
|
const filterAffixTags = (routes: RouteRecordRaw[], basePath = '/') => {
|
||||||
return route.path === this.$route.path
|
let tags: TagView[] = []
|
||||||
},
|
|
||||||
isAffix(tag) {
|
|
||||||
return tag.meta && tag.meta.affix
|
|
||||||
},
|
|
||||||
filterAffixTags(routes, basePath = '/') {
|
|
||||||
let tags = []
|
|
||||||
routes.forEach(route => {
|
routes.forEach(route => {
|
||||||
if (route.meta && route.meta.affix) {
|
if (route.meta && route.meta.affix) {
|
||||||
const tagPath = path.resolve(basePath, route.path)
|
const tagPath = path.resolve(basePath, route.path)
|
||||||
|
|
@ -86,117 +148,81 @@ export default {
|
||||||
meta: { ...route.meta }
|
meta: { ...route.meta }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route.children) {
|
if (route.children) {
|
||||||
const tempTags = this.filterAffixTags(route.children, route.path)
|
const childTags = filterAffixTags(route.children, route.path)
|
||||||
if (tempTags.length >= 1) {
|
if (childTags.length >= 1) {
|
||||||
tags = [...tags, ...tempTags]
|
tags = tags.concat(childTags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return tags
|
return tags
|
||||||
},
|
}
|
||||||
initTags() {
|
|
||||||
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
|
const initTags = () => {
|
||||||
for (const tag of affixTags) {
|
state.affixTags = filterAffixTags(routes.value)
|
||||||
|
for (const tag of state.affixTags) {
|
||||||
// Must have tag name
|
// Must have tag name
|
||||||
if (tag.name) {
|
if (tag.name) {
|
||||||
this.$store.dispatch('tagsView/addVisitedView', tag)
|
store.dispatch('tagsView/addVisitedView', tag as TagView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
addTags() {
|
|
||||||
const { name } = this.$route
|
const addTags = () => {
|
||||||
if (name) {
|
if (currentRoute.name) {
|
||||||
this.$store.dispatch('tagsView/addView', this.$route)
|
store.dispatch('tagsView/addView', currentRoute)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
}
|
||||||
moveToCurrentTag() {
|
|
||||||
const tags = this.$refs.tag
|
const moveToCurrentTag = () => {
|
||||||
this.$nextTick(() => {
|
const tags = instance?.refs.tag as any[]
|
||||||
|
nextTick(() => {
|
||||||
|
if (tags === null || tags === undefined || !Array.isArray(tags)) { return }
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
if (tag.to.path === this.$route.path) {
|
if ((tag.to as TagView).path === currentRoute.path) {
|
||||||
this.$refs.scrollPane.moveToTarget(tag)
|
(scrollPaneRef.value as any).moveToCurrentTag(tag)
|
||||||
// when query is different then update
|
// When query is different then update
|
||||||
if (tag.to.fullPath !== this.$route.fullPath) {
|
if ((tag.to as TagView).fullPath !== currentRoute.fullPath) {
|
||||||
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
|
store.dispatch('tagsView/updateVisitedView', currentRoute)
|
||||||
}
|
}
|
||||||
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.fullPath)
|
|
||||||
} 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) {
|
watch(() => currentRoute.name, () => {
|
||||||
this.left = maxLeft
|
if (currentRoute.name !== 'Login') {
|
||||||
} else {
|
addTags()
|
||||||
this.left = left
|
moveToCurrentTag()
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.top = e.clientY
|
watch(() => state.visible, (value) => {
|
||||||
this.visible = true
|
if (value) {
|
||||||
this.selectedTag = tag
|
document.body.addEventListener('click', state.closeMenu)
|
||||||
},
|
} else {
|
||||||
closeMenu() {
|
document.body.removeEventListener('click', state.closeMenu)
|
||||||
this.visible = false
|
}
|
||||||
},
|
})
|
||||||
handleScroll() {
|
|
||||||
this.closeMenu()
|
// life cricle
|
||||||
|
onBeforeMount(() => {
|
||||||
|
initTags()
|
||||||
|
addTags()
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
visitedViews,
|
||||||
|
routes,
|
||||||
|
scrollPaneRef,
|
||||||
|
...toRefs(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
@ -206,6 +232,7 @@ export default {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-bottom: 1px solid #d8dce5;
|
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);
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||||
|
|
||||||
.tags-view-wrapper {
|
.tags-view-wrapper {
|
||||||
.tags-view-item {
|
.tags-view-item {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
@ -220,16 +247,20 @@ export default {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
|
||||||
&:first-of-type {
|
&:first-of-type {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: #42b983;
|
background-color: #42b983;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-color: #42b983;
|
border-color: #42b983;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: '';
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
@ -243,6 +274,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.contextmenu {
|
.contextmenu {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
@ -255,10 +287,12 @@ export default {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #333;
|
color: #333;
|
||||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 7px 16px;
|
padding: 7px 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #eee;
|
background: #eee;
|
||||||
}
|
}
|
||||||
|
|
@ -279,11 +313,13 @@ export default {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: all .3s cubic-bezier(.645, .045, .355, 1);
|
transition: all .3s cubic-bezier(.645, .045, .355, 1);
|
||||||
transform-origin: 100% 50%;
|
transform-origin: 100% 50%;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
transform: scale(.6);
|
transform: scale(.6);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: -3px;
|
vertical-align: -3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #b4bccc;
|
background-color: #b4bccc;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
export { default as Navbar } from './Navbar.vue'
|
export { default as Navbar } from './Navbar.vue'
|
||||||
export { default as Sidebar } from './Sidebar/index.vue'
|
export { default as Sidebar } from './Sidebar/index.vue'
|
||||||
export { default as AppMain } from './AppMain.vue'
|
export { default as AppMain } from './AppMain.vue'
|
||||||
|
export { default as Settings } from './Settings/index.vue'
|
||||||
|
export { default as TagsView } from './TagsView/index.vue'
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="classObj" class="app-wrapper">
|
<div :class="classObj" class="app-wrapper">
|
||||||
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
|
||||||
<sidebar class="sidebar-container" />
|
<sidebar class="sidebar-container"/>
|
||||||
<div :class="{hasTagsView:needTagsView}" class="main-container">
|
<div :class="{hasTagsView:needTagsView}" class="main-container">
|
||||||
<div :class="{'fixed-header':fixedHeader}">
|
<div :class="{'fixed-header':fixedHeader}">
|
||||||
<navbar />
|
<navbar/>
|
||||||
<tags-view v-if="needTagsView" />
|
<tags-view v-if="needTagsView"/>
|
||||||
</div>
|
</div>
|
||||||
<app-main />
|
<app-main/>
|
||||||
<right-panel v-if="showSettings">
|
<right-panel v-if="showSettings">
|
||||||
<settings />
|
<settings/>
|
||||||
</right-panel>
|
</right-panel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -18,27 +18,30 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {computed, defineComponent, onBeforeMount, onBeforeUnmount, onMounted, reactive, toRefs} from "vue";
|
import {computed, defineComponent, onBeforeMount, onBeforeUnmount, onMounted, reactive, toRefs} from "vue";
|
||||||
import {Navbar, Sidebar, AppMain} from './components'
|
import {AppMain,Navbar, Settings,Sidebar,TagsView } from './components'
|
||||||
import {
|
import resize from './mixin/ResizeHandler'
|
||||||
sidebar,
|
|
||||||
device,
|
|
||||||
resizeMounted,
|
|
||||||
addEventListenerOnResize,
|
|
||||||
removeEventListenerResize,
|
|
||||||
watchRouter
|
|
||||||
} from './mixin/ResizeHandler'
|
|
||||||
import {useStore} from "@store";
|
import {useStore} from "@store";
|
||||||
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Layout',
|
name: 'Layout',
|
||||||
components: {
|
components: {
|
||||||
|
AppMain,
|
||||||
Navbar,
|
Navbar,
|
||||||
|
Settings,
|
||||||
Sidebar,
|
Sidebar,
|
||||||
AppMain
|
TagsView
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
const {
|
||||||
|
sidebar,
|
||||||
|
device,
|
||||||
|
resizeMounted,
|
||||||
|
addEventListenerOnResize,
|
||||||
|
removeEventListenerResize,
|
||||||
|
watchRouter
|
||||||
|
} = resize()
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
handleClickOutside: () => {
|
handleClickOutside: () => {
|
||||||
|
|
@ -54,8 +57,41 @@ export default defineComponent({
|
||||||
mobile: device === 'mobile'
|
mobile: device === 'mobile'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
const showSettings=computed(()=>{
|
||||||
|
return store.state.settings.showSettings
|
||||||
|
})
|
||||||
|
|
||||||
|
const needTagsView=computed(()=>{
|
||||||
|
return store.state.settings.tagsView
|
||||||
|
})
|
||||||
|
|
||||||
|
const fixedHeader=computed(()=>{
|
||||||
|
return store.state.settings.fixedHeader
|
||||||
|
})
|
||||||
|
|
||||||
|
watchRouter()
|
||||||
|
|
||||||
|
onBeforeMount(()=>{
|
||||||
|
addEventListenerOnResize()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
resizeMounted()
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(()=>{
|
||||||
|
removeEventListenerResize()
|
||||||
|
})
|
||||||
|
|
||||||
|
return{
|
||||||
|
classObj,
|
||||||
|
sidebar,
|
||||||
|
showSettings,
|
||||||
|
needTagsView,
|
||||||
|
fixedHeader,
|
||||||
|
...toRefs(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ export default function () {
|
||||||
window.removeEventListener('resize', resizeHandler)
|
window.removeEventListener('resize', resizeHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const currentRoute = useRoute()
|
const currentRoute = useRoute()
|
||||||
const watchRouter = watch(() => currentRoute.name, () => {
|
const watchRouter = watch(() => currentRoute.name, () => {
|
||||||
if (store.state.app.device === 'mobile' && store.state.app.sidebar.opened) {
|
if (store.state.app.device === 'mobile' && store.state.app.sidebar.opened) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {RouteRecordRaw} from "vue-router";
|
import {RouteRecordRaw,RouteLocationNormalized} from "vue-router";
|
||||||
|
|
||||||
// 接口类型声明
|
// 接口类型声明
|
||||||
export interface UserState {
|
export interface UserState {
|
||||||
|
|
@ -32,10 +32,21 @@ export interface PermissionState{
|
||||||
addRoutes: RouteRecordRaw[]
|
addRoutes: RouteRecordRaw[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TagView extends Partial<RouteLocationNormalized> {
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TagsViewState{
|
||||||
|
visitedViews: TagView[],
|
||||||
|
cachedViews: (string|undefined)[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 顶级类型声明
|
// 顶级类型声明
|
||||||
export interface RootStateTypes {
|
export interface RootStateTypes {
|
||||||
user: UserState,
|
user: UserState,
|
||||||
app: AppState,
|
app: AppState,
|
||||||
setting: SettingState,
|
setting: SettingState,
|
||||||
permission:PermissionState
|
permission:PermissionState,
|
||||||
|
tagsView:TagsViewState
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
|
||||||
|
import {Module} from "vuex";
|
||||||
|
import {TagsViewState,RootStateTypes} from "@store/interface";
|
||||||
|
|
||||||
|
const tagsViewModule: Module<TagsViewState, RootStateTypes> = {
|
||||||
|
namespaced: true,
|
||||||
|
state: {
|
||||||
|
visitedViews: [],
|
||||||
|
cachedViews: []
|
||||||
|
},
|
||||||
|
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.name)) return
|
||||||
|
if (!view.meta.noCache) {
|
||||||
|
state.cachedViews.push(view.name)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
const index = state.cachedViews.indexOf(view.name)
|
||||||
|
index > -1 && state.cachedViews.splice(index, 1)
|
||||||
|
},
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
const index = state.cachedViews.indexOf(view.name)
|
||||||
|
if (index > -1) {
|
||||||
|
state.cachedViews = state.cachedViews.slice(index, index + 1)
|
||||||
|
} else {
|
||||||
|
// if index = -1, there is no cached tags
|
||||||
|
state.cachedViews = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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 tagsViewModule;
|
||||||
|
|
||||||
Loading…
Reference in New Issue