You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

374 lines
9.5 KiB

<template lang="pug">
div
.pa-3.d-flex(:class='$vuetify.theme.dark ? `grey darken-5` : `blue darken-3`')
v-btn.ml-2(
depressed
:color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`'
style='min-width:0;'
@click='goHome'
:aria-label='$t(`common:header.home`)'
)
v-icon(size='20') mdi-home
v-btn.ml-3(
depressed
:color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`'
style='min-width:0;'
@click='goStudentProfile'
:aria-label='$t(`common:header.home`)'
)
v-icon(size='20') mdi-account-box
v-btn.ml-2(
depressed
:color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`'
style='flex: 1 1 100%;'
@click='goPlan'
:aria-label='$t(`common:header.home`)'
)
v-icon(size='20') mdi-book-education
v-divider
//-> Tree Navigation
v-treeview(
activatable
dense
open-on-click
:color='"white"'
:active='treeDefaultActive'
:open='treeDefaultOpen'
:items='treeItems'
:load-children='fetchTreeChild'
@update:active='activeTreeItem'
)
template(v-slot:prepend="{ item, open }")
v-icon(v-if="open") mdi-folder-open
v-icon(v-else) mdi-{{ item.icon }}
template(v-slot:label="{ item }")
div(class='tree-item')
a(v-if="!item.children" :href="'/'+item.locale+'/'+item.path")
span {{item.name}}
span(v-else) {{item.name}}
</template>
<script>
import _ from 'lodash'
import gql from 'graphql-tag'
import { get } from 'vuex-pathify'
/* global siteLangs */
export default {
props: {
color: {
type: String,
default: 'primary'
},
dark: {
type: Boolean,
default: true
},
items: {
type: Array,
default: () => []
},
navMode: {
type: String,
default: 'MIXED'
}
},
data() {
return {
currentItems: [],
currentParent: {
id: 0,
title: '/ (root)'
},
parents: [],
loadedCache: [],
treeItems: [],
treeDefaultOpen: [],
treeDefaultActive: []
}
},
computed: {
path: get('page/path'),
locale: get('page/locale')
},
methods: {
async fetchBrowseItems (item) {
this.$store.commit(`loadingStart`, 'browse-load')
if (!item) {
item = this.currentParent
}
if (this.loadedCache.indexOf(item.id) < 0) {
this.currentItems = []
}
if (item.id === 0) {
this.parents = []
} else {
const flushRightIndex = _.findIndex(this.parents, ['id', item.id])
if (flushRightIndex >= 0) {
this.parents = _.take(this.parents, flushRightIndex)
}
if (this.parents.length < 1) {
this.parents.push(this.currentParent)
}
this.parents.push(item)
}
this.currentParent = item
const resp = await this.$apollo.query({
query: gql`
query ($parent: Int, $locale: String!) {
pages {
tree(parent: $parent, mode: ALL, locale: $locale) {
id
path
title
isFolder
pageId
parent
locale
}
}
}
`,
fetchPolicy: 'cache-first',
variables: {
parent: item.id,
locale: this.locale
}
})
this.loadedCache = _.union(this.loadedCache, [item.id])
this.currentItems = _.get(resp, 'data.pages.tree', [])
this.$store.commit(`loadingStop`, 'browse-load')
},
async loadFromCurrentPath() {
this.$store.commit(`loadingStart`, 'browse-load')
const resp = await this.$apollo.query({
query: gql`
query ($path: String, $locale: String!) {
pages {
tree(path: $path, mode: ALL, locale: $locale, includeAncestors: true) {
id
path
title
isFolder
pageId
parent
locale
}
}
}
`,
fetchPolicy: 'cache-first',
variables: {
path: this.path,
locale: this.locale
}
})
const items = _.get(resp, 'data.pages.tree', [])
const curPage = _.find(items, ['pageId', this.$store.get('page/id')])
if (!curPage) {
console.warn('Could not find current page in page tree listing!')
return
}
let curParentId = curPage.parent
let invertedAncestors = []
while (curParentId) {
const curParent = _.find(items, ['id', curParentId])
if (!curParent) {
break
}
invertedAncestors.push(curParent)
curParentId = curParent.parent
}
this.parents = [this.currentParent, ...invertedAncestors.reverse()]
this.currentParent = _.last(this.parents)
this.loadedCache = [curPage.parent]
this.currentItems = _.filter(items, ['parent', curPage.parent])
this.$store.commit(`loadingStop`, 'browse-load')
if (curPage.isFolder) {
await this.fetchBrowseItems(curPage)
}
},
goHome (event) {
const url = siteLangs.length > 0 ? `/${this.locale}/home` : '/'
if (event.ctrlKey || event.metaKey) {
// Если зажат Ctrl или Cmd (на Mac), открываем в новом окне
window.open(url, '_blank')
} else {
// Иначе открываем в текущем окне
window.location.assign(url)
}
},
goStudentProfile (event) {
const url = '/Users/profile'
if (event.ctrlKey || event.metaKey) {
window.open(url, '_blank')
} else {
window.location.assign(url)
}
},
goPlan (event) {
const url = '/plan'
if (event.ctrlKey || event.metaKey) {
window.open(url, '_blank')
} else {
window.location.assign(url)
}
},
pageItem2TreeItem(item, level) {
if (item.isFolder) {
return { id: item.id, icon: item.icon, level: level, pageId: item.pageId, path: item.path, locale: item.locale, name: item.title, children: [] }
} else {
return { id: item.id, icon: item.icon, level: level, path: item.path, locale: item.locale, name: item.title }
}
},
activeTreeItem([id]) {
const find = (items) => {
for (const item of items) {
if (item.id === id) {
return item
}
if (item.children && item.children.length) {
const v = find(item.children)
if (v) {
return v
}
}
}
}
const item = find(this.treeItems)
if (item) {
if (!this.treeDefaultActive.includes(item.id)) {
location.href = `/${item.locale}/${item.path}`
} else {
setTimeout(() => {
const el = document.querySelector('.v-treeview-node--active')
el.scrollIntoViewIfNeeded()
})
}
}
},
async fetchTreeChild(parent) {
const items = await this.fetchPages(parent.id)
parent.children = []
if (parent.pageId) {
parent.children.push({
id: parent.pageId,
level: parent.level + 1,
icon: parent.icon,
path: parent.path,
locale: parent.locale,
name: parent.name
})
}
parent.children.push(
...items.map(item => this.pageItem2TreeItem(item, parent.level + 1))
)
this.checkTreeDefaultOpen(parent.children)
},
async fetchTreeRoot() {
const children = await this.fetchPages(0)
this.treeItems = children.map(item => this.pageItem2TreeItem(item, 0))
this.checkTreeDefaultOpen(this.treeItems, 0)
},
checkTreeDefaultOpen(items) {
const autoOpenBlacklist = 'Users'
const item = items.find(item => {
if (item.path.startsWith(autoOpenBlacklist)) {
return false
}
return item.children && this.path.startsWith(item.path)
})
if (item) {
setTimeout(() => {
this.treeDefaultOpen.push(item.id)
})
}
const active = items.find(item => item.path === this.path)
if (active) {
this.treeDefaultActive.push(active.id)
}
},
async fetchPages(id) {
const resp = await this.$apollo.query({
query: gql`
query($parent: Int, $locale: String!) {
pages {
tree(parent: $parent, mode: ALL, locale: $locale) {
id
path
title
icon
isFolder
pageId
parent
locale
}
}
}
`,
fetchPolicy: 'cache-first',
variables: {
parent: id,
locale: this.locale
}
})
return _.get(resp, 'data.pages.tree', [])
}
},
mounted () {
this.currentParent.title = `/ ${this.$t('common:sidebar.root')}`
this.fetchTreeRoot()
}
}
</script>
<style lang="scss">
.v-treeview{
.tree-item {
font-weight: 500;
line-height: 1rem;
font-size: 0.9rem;
}
a {
text-decoration: none;
}
&.theme--dark{
a {
color: white;
}
}
}
.v-treeview-node {
min-height: 32px;
.v-treeview-node__root {
min-height: 32px;
padding: 1px 0;
}
.v-treeview-node__content {
margin: 1px 0;
}
.v-treeview-node__children {
margin-left: 16px;
}
}
</style>