feat(frontend): many things
really too many things changed for a commit message. just build this
This commit is contained in:
Generated
+295
-514
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,7 @@
|
||||
"vue-matomo": "^3.14.0-0",
|
||||
"vue-router": "^3.4.9",
|
||||
"vue-timeago": "^5.1.2",
|
||||
"vue2-perfect-scrollbar": "^1.5.2",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuetify": "^2.3.21",
|
||||
"vuetify-image-input": "^19.1.0",
|
||||
|
||||
@@ -21,6 +21,11 @@ Vue.use(VueFilterDateParse)
|
||||
import VueFilterDateFormat from '@vuejs-community/vue-filter-date-format'
|
||||
Vue.use(VueFilterDateFormat)
|
||||
|
||||
import PerfectScrollbar from 'vue2-perfect-scrollbar'
|
||||
import 'vue2-perfect-scrollbar/dist/vue2-perfect-scrollbar.css'
|
||||
|
||||
Vue.use(PerfectScrollbar)
|
||||
|
||||
import VTooltip from 'v-tooltip'
|
||||
Vue.use(VTooltip, { defaultDelay: 300 })
|
||||
|
||||
@@ -53,6 +58,7 @@ Vue.filter('capitalize', (value) => {
|
||||
return value.charAt(0).toUpperCase() + value.slice(1)
|
||||
})
|
||||
|
||||
// Event hub
|
||||
Vue.prototype.$eventHub = new Vue()
|
||||
|
||||
// adds copy to clipboard on vue instance
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
:class="`grey ${$vuetify.theme.dark ? 'darken-4' : 'lighten-4'} elevation-1`"
|
||||
width="325"
|
||||
>
|
||||
<main-nav />
|
||||
<!-- <perfect-scrollbar :options="{ suppressScrollX: true }"> -->
|
||||
<main-nav />
|
||||
<!-- </perfect-scrollbar> -->
|
||||
<template v-if="$route.meta.showBottomNavActions" #append>
|
||||
<main-nav-bottom />
|
||||
</template>
|
||||
@@ -84,3 +86,14 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.ps {
|
||||
height: 100%;
|
||||
-ms-overflow-style: none; /* for Internet Explorer, Edge */
|
||||
scrollbar-width: none; /* for Firefox */
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.ps::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div class="mt-3">
|
||||
<portal to="filter-actions">
|
||||
<v-list-item-action class="pa-0 ma-0">
|
||||
<v-btn v-tooltip="''" small icon @click.stop="colorBy = !colorBy">
|
||||
<v-icon small :class="`${colorBy ? 'primary--text' : ''}`">mdi-palette</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
</portal>
|
||||
<v-row
|
||||
v-for="type in typeMap"
|
||||
:key="type.fullName"
|
||||
no-gutters
|
||||
class="my-1 property-row rounded-lg"
|
||||
>
|
||||
<v-col
|
||||
cols="1"
|
||||
:class="`text-center text-truncate px-1 ${$vuetify.theme.dark ? 'grey--text' : ''}`"
|
||||
style="line-height: 24px; font-size: 9px"
|
||||
>
|
||||
{{ type.count }}
|
||||
</v-col>
|
||||
<v-col
|
||||
v-tooltip="type.fullName"
|
||||
cols="8"
|
||||
:class="`caption text-truncate px-1 ${$vuetify.theme.dark ? 'grey--text' : ''}`"
|
||||
style="line-height: 24px"
|
||||
>
|
||||
{{ type.name }}
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="3"
|
||||
:class="`caption text-truncate text-right px-1 ${$vuetify.theme.dark ? 'grey--text' : ''}`"
|
||||
style="line-height: 24px"
|
||||
>
|
||||
<div
|
||||
v-if="colorBy"
|
||||
class="d-inline-block rounded"
|
||||
:style="`width: 10px; height: 10px`"
|
||||
></div>
|
||||
<v-btn
|
||||
v-tooltip="'Toggle visibility'"
|
||||
x-small
|
||||
icon
|
||||
class="mr-1"
|
||||
@click="toggleVisibility(type.fullName)"
|
||||
>
|
||||
<v-icon class="grey--text" style="font-size: 11px">
|
||||
{{ hidden.indexOf(type.fullName) === -1 ? 'mdi-eye' : 'mdi-eye-off' }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-tooltip="'Isolate objects'"
|
||||
x-small
|
||||
icon
|
||||
class="mr-1"
|
||||
@click="toggleFilter(type.fullName)"
|
||||
>
|
||||
<v-icon
|
||||
:class="`${filtered.indexOf(type.fullName) !== -1 ? 'primary--text' : 'grey--text'}`"
|
||||
style="font-size: 11px"
|
||||
>
|
||||
{{ !filtered.indexOf(type.fullName) !== -1 ? 'mdi-filter' : 'mdi-filter' }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
props: {
|
||||
filter: {
|
||||
type: Object,
|
||||
default: () => null
|
||||
},
|
||||
active: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hidden: [],
|
||||
filtered: [],
|
||||
typeMap: [],
|
||||
colorBy: false,
|
||||
appliedFilter: {},
|
||||
legend: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
filter(newVal) {
|
||||
this.generateTypeMap(newVal)
|
||||
},
|
||||
async colorBy(newVal) {
|
||||
this.appliedFilter.colorBy = newVal
|
||||
? { type: 'category', property: this.filter.targetKey }
|
||||
: null
|
||||
let res = await window.__viewer.applyFilter(this.appliedFilter)
|
||||
console.log(res)
|
||||
this.mashColorLegend(res.colorLegend)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.generateTypeMap(this.filter)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.__viewer.applyFilter(null)
|
||||
},
|
||||
methods: {
|
||||
mashColorLegend(thanksCristi) {
|
||||
if (!thanksCristi) return
|
||||
// TODO
|
||||
},
|
||||
async toggleFilter(type) {
|
||||
let indx = this.filtered.indexOf(type)
|
||||
if (indx === -1) this.filtered.push(type)
|
||||
else this.filtered.splice(indx, 1)
|
||||
this.hidden.splice(0, this.hidden.length)
|
||||
if (this.filtered.length === 0) {
|
||||
window.__viewer.applyFilter(
|
||||
this.colorBy ? { colorBy: { type: 'category', property: this.filter.targetKey } } : null
|
||||
)
|
||||
this.appliedFilter = {}
|
||||
} else {
|
||||
let filterObj = {
|
||||
filterBy: {},
|
||||
colorBy: this.colorBy ? { type: 'category', property: this.filter.targetKey } : null,
|
||||
ghostOthers: true
|
||||
}
|
||||
filterObj.filterBy[this.filter.targetKey] = this.filtered
|
||||
let res = await window.__viewer.applyFilter(filterObj)
|
||||
this.mashColorLegend(res.colorLegend)
|
||||
this.appliedFilter = filterObj
|
||||
}
|
||||
},
|
||||
async toggleVisibility(type) {
|
||||
let indx = this.hidden.indexOf(type)
|
||||
if (indx === -1) this.hidden.push(type)
|
||||
else this.hidden.splice(indx, 1)
|
||||
this.filtered.splice(0, this.filtered.length)
|
||||
if (this.hidden.length === 0) {
|
||||
window.__viewer.applyFilter(
|
||||
this.colorBy ? { colorBy: { type: 'category', property: this.filter.targetKey } } : null
|
||||
)
|
||||
this.appliedFilter = {}
|
||||
} else {
|
||||
let filterObj = {
|
||||
filterBy: {},
|
||||
colorBy: this.colorBy ? { type: 'category', property: this.filter.targetKey } : null,
|
||||
ghostOthers: false
|
||||
}
|
||||
filterObj.filterBy[this.filter.targetKey] = { not: this.hidden }
|
||||
let res = await window.__viewer.applyFilter(filterObj)
|
||||
this.mashColorLegend(res.colorLegend)
|
||||
this.appliedFilter = filterObj
|
||||
}
|
||||
},
|
||||
generateTypeMap(filter) {
|
||||
if (filter.data.type !== 'string') return []
|
||||
let typeMap = []
|
||||
for (let key of Object.keys(filter.data.uniqueValues)) {
|
||||
let shortName = key.split('.').reverse()[0]
|
||||
typeMap.push({
|
||||
name: shortName,
|
||||
fullName: key,
|
||||
count: filter.data.uniqueValues[key]
|
||||
})
|
||||
}
|
||||
this.typeMap.splice(0, this.typeMap.length)
|
||||
this.typeMap.push(...typeMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.property-row {
|
||||
transition: all 0.3s ease;
|
||||
background: rgba(120, 120, 120, 0.05);
|
||||
}
|
||||
.property-row:hover {
|
||||
background: rgba(120, 120, 120, 0.09);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<v-row no-gutters class="my-1 property-row rounded-lg">
|
||||
<v-col cols="1" class="text-center" style="line-height: 30px">
|
||||
<v-icon small style="font-size: 12px" :class="`${$vuetify.theme.dark ? 'grey--text' : ''}`">
|
||||
{{ filter.data.type === 'number' ? 'mdi-numeric' : 'mdi-format-text' }}
|
||||
</v-icon>
|
||||
</v-col>
|
||||
<v-col
|
||||
v-tooltip="filter.targetKey"
|
||||
cols="8"
|
||||
:class="`caption text-truncate px-1 ${$vuetify.theme.dark ? 'grey--text' : ''}`"
|
||||
style="line-height: 30px"
|
||||
>
|
||||
{{ filter.name }}
|
||||
</v-col>
|
||||
<v-col class="text-right" style="line-height: 30px">
|
||||
<v-btn x-small @click="$emit('active-toggle', filter)">{{ active ? 'Remove' : 'Set' }}</v-btn>
|
||||
</v-col>
|
||||
<v-scroll-y-transition>
|
||||
<v-col v-if="active && filter.data.type === 'string'" cols="12">
|
||||
<!-- <div v-if="filter.data.type === 'string'"> -->
|
||||
<v-row
|
||||
v-for="type in typeMap"
|
||||
:key="type.fullName"
|
||||
no-gutters
|
||||
class="my-1 property-row rounded-lg"
|
||||
>
|
||||
<v-col
|
||||
cols="1"
|
||||
:class="`caption text-center text-truncate px-1 ${
|
||||
$vuetify.theme.dark ? 'grey--text' : ''
|
||||
}`"
|
||||
style="line-height: 24px; font-size: 10px"
|
||||
>
|
||||
{{ type.count }}
|
||||
</v-col>
|
||||
<v-col
|
||||
v-tooltip="type.fullName"
|
||||
cols="8"
|
||||
:class="`caption text-truncate px-1 ${$vuetify.theme.dark ? 'grey--text' : ''}`"
|
||||
style="line-height: 24px"
|
||||
>
|
||||
{{ type.name }}
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="3"
|
||||
:class="`caption text-truncate text-right px-1 ${
|
||||
$vuetify.theme.dark ? 'grey--text' : ''
|
||||
}`"
|
||||
style="line-height: 24px"
|
||||
>
|
||||
<v-btn
|
||||
v-tooltip="'Toggle visibility'"
|
||||
x-small
|
||||
icon
|
||||
class="mr-1"
|
||||
@click="toggleVisibility()"
|
||||
>
|
||||
<v-icon class="grey--text" style="font-size: 11px">
|
||||
{{ visible ? 'mdi-eye' : 'mdi-eye-off' }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-tooltip="'Isolate objects'" x-small icon class="mr-1" @click="toggleFilter()">
|
||||
<v-icon
|
||||
:class="`${filtered ? 'primary--text' : 'grey--text'}`"
|
||||
style="font-size: 11px"
|
||||
>
|
||||
{{ !filtered ? 'mdi-filter' : 'mdi-filter' }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<!-- </div> -->
|
||||
</v-col>
|
||||
</v-scroll-y-transition>
|
||||
</v-row>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
props: {
|
||||
filter: {
|
||||
type: Object,
|
||||
default: () => null
|
||||
},
|
||||
active: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: true,
|
||||
filtered: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
typeMap() {
|
||||
if (this.filter.data.type !== 'string') return []
|
||||
let typeMap = []
|
||||
for (let key of Object.keys(this.filter.data.uniqueValues)) {
|
||||
let shortName = key.split('.').reverse()[0]
|
||||
typeMap.push({
|
||||
name: shortName,
|
||||
fullName: key,
|
||||
count: this.filter.data.uniqueValues[key]
|
||||
})
|
||||
}
|
||||
return typeMap
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.property-row {
|
||||
transition: all 0.3s ease;
|
||||
background: rgba(120, 120, 120, 0.05);
|
||||
}
|
||||
.property-row:hover {
|
||||
background: rgba(120, 120, 120, 0.09);
|
||||
}
|
||||
</style>
|
||||
@@ -1,54 +1,196 @@
|
||||
<template>
|
||||
<v-list dense nav class="mt-0 py-0">
|
||||
<v-list-item :class="`px-2 list-overlay`" active @click="expand = !expand">
|
||||
<v-list dense nav class="mt-0 py-0 mb-3">
|
||||
<v-list-item
|
||||
:class="`px-2 list-overlay-${$vuetify.theme.dark ? 'dark' : 'light'} elevation-2`"
|
||||
style="position: sticky; top: 64px"
|
||||
@click="expand = !expand"
|
||||
>
|
||||
<v-list-item-action>
|
||||
<v-icon small>mdi-filter-variant</v-icon>
|
||||
</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Filters</v-list-item-title>
|
||||
<v-list-item-title>
|
||||
<span v-if="activeFilter === null">
|
||||
Filters
|
||||
<span class="caption grey--text">({{ allFilters.length }})</span>
|
||||
</span>
|
||||
<span v-else>{{ activeFilter.name }}</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
<portal-target name="filter-actions"></portal-target>
|
||||
<v-list-item-action v-if="activeFilter" class="pa-0 ma-0">
|
||||
<v-btn
|
||||
v-tooltip="'Remove filter'"
|
||||
small
|
||||
icon
|
||||
@click.stop="
|
||||
activeFilter = null
|
||||
filterSearch = null
|
||||
"
|
||||
>
|
||||
<v-icon small>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
<v-list-item-action class="pa-0 ma-0">
|
||||
<v-btn small icon @click.stop="expand = !expand">
|
||||
<v-icon>{{ expand ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
<v-expand-transition>
|
||||
<v-scroll-y-transition>
|
||||
<div v-show="expand">
|
||||
<v-subheader>Suggested Filters</v-subheader>
|
||||
<div class="px-2">Todo</div>
|
||||
<v-subheader>All filters</v-subheader>
|
||||
<div class="px-1">
|
||||
<v-text-field
|
||||
solo
|
||||
dense
|
||||
placeholder="Search filters"
|
||||
append-icon="mdi-magnify"
|
||||
hide-details
|
||||
/>
|
||||
<div v-if="activeFilter" class="px-0">
|
||||
<filter-active :filter="activeFilter" />
|
||||
</div>
|
||||
<div v-show="activeFilter === null">
|
||||
<v-subheader>TODO: reccommended filters</v-subheader>
|
||||
<div class="">
|
||||
<v-text-field
|
||||
v-model="filterSearch"
|
||||
solo
|
||||
dense
|
||||
placeholder="Search filters"
|
||||
append-icon="mdi-magnify"
|
||||
hide-details
|
||||
class="my-2"
|
||||
style="position: sticky; top: 110px; z-index: 6"
|
||||
/>
|
||||
<div v-for="filter in matchingFilters" :key="filter.targetKey">
|
||||
<filter-single :filter="filter" @active-toggle="(e) => (activeFilter = e)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-expand-transition>
|
||||
</v-scroll-y-transition>
|
||||
</v-list>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
components: {
|
||||
FilterSingle: () => import('@/cleanup/components/viewer/FilterSingle'),
|
||||
FilterActive: () => import('@/cleanup/components/viewer/FilterActive')
|
||||
},
|
||||
props: {
|
||||
views: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
props: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
sourceApplication: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expand: true
|
||||
expand: true,
|
||||
defaultFilters: [{ targetKey: 'speckle_type', name: 'Speckle Type' }],
|
||||
revitFilters: ['type', 'family', 'level'],
|
||||
allFilters: [],
|
||||
activeFilter: null,
|
||||
filterSearch: null
|
||||
}
|
||||
},
|
||||
computed: {}
|
||||
computed: {
|
||||
matchingFilters() {
|
||||
if (this.filterSearch === null) return this.allFilters
|
||||
else {
|
||||
return this.allFilters.filter(
|
||||
(f) =>
|
||||
f.name.toLowerCase().includes(this.filterSearch.toLowerCase()) ||
|
||||
f.targetKey.toLowerCase().includes(this.filterSearch.toLowerCase())
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
props(newVal) {
|
||||
if (newVal) this.parseAndSetFilters()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.props) {
|
||||
this.parseAndSetFilters()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
parseAndSetFilters() {
|
||||
let keys = Object.keys(this.props)
|
||||
let filters = []
|
||||
for (let key of keys) {
|
||||
let filter = {}
|
||||
// Handle revit params
|
||||
if (key.startsWith('parameters.')) {
|
||||
if (key.endsWith('.value')) {
|
||||
filter.name = this.props[key.replace('.value', '.name')].allValues[0]
|
||||
filter.targetKey = key
|
||||
filter.data = this.props[key]
|
||||
filters.push(filter)
|
||||
continue
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Beautify level name
|
||||
if (key === 'level.name') {
|
||||
filter.name = 'Level'
|
||||
filter.targetKey = key
|
||||
filter.data = this.props[key]
|
||||
filters.push(filter)
|
||||
continue
|
||||
}
|
||||
// Beautify speckle type
|
||||
if (key === 'speckle_type') {
|
||||
filter.name = 'Object Type'
|
||||
filter.targetKey = key
|
||||
filter.data = this.props[key]
|
||||
filters.push(filter)
|
||||
continue
|
||||
}
|
||||
// Skip some
|
||||
if (
|
||||
key.endsWith('.units') ||
|
||||
key.endsWith('.speckle_type') ||
|
||||
key.includes('.parameters.') ||
|
||||
key.includes('level.') ||
|
||||
key.includes('renderMaterial') ||
|
||||
key.includes('.domain') ||
|
||||
key.includes('plane.') ||
|
||||
key.includes('baseLine') ||
|
||||
key.includes('referenceLine') ||
|
||||
key.includes('end.') ||
|
||||
key.includes('start.') ||
|
||||
key.includes('endPoint.') ||
|
||||
key.includes('midPoint.') ||
|
||||
key.includes('startPoint.') ||
|
||||
key.includes('startPoint.') ||
|
||||
key.includes('displayStyle') ||
|
||||
key.includes('displayValue') ||
|
||||
key.includes('displayMesh')
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
filter.name = key
|
||||
filter.targetKey = key
|
||||
filter.data = this.props[key]
|
||||
filters.push(filter)
|
||||
}
|
||||
console.log(filters)
|
||||
this.allFilters = filters
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.list-overlay {
|
||||
background: rgba(120, 120, 120, 0.09);
|
||||
.list-overlay-dark {
|
||||
background: rgba(40, 40, 40, 1);
|
||||
z-index: 5;
|
||||
}
|
||||
.list-overlay-light {
|
||||
background: rgba(235, 235, 235, 1);
|
||||
z-index: 5;
|
||||
}
|
||||
.ps {
|
||||
height: 50vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
<v-card class="transparent elevation-5 rounded-md overflow-hidden d-inline-block">
|
||||
<v-btn
|
||||
v-show="showVisReset"
|
||||
v-tooltip="`Reset visibility`"
|
||||
v-tooltip="`Resets all applied filters`"
|
||||
tile
|
||||
small
|
||||
@click="resetVisibility()"
|
||||
>
|
||||
<v-icon small>mdi-filter-off-outline</v-icon>
|
||||
<!-- <v-icon small>mdi-filter-off-outline</v-icon> -->
|
||||
Show All
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-tooltip="`Toggle between perspective or ortho camera.`"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<v-card
|
||||
class="space-grotesk primary--text text-h6 pt-5 mb-2 px-5 elevation-0"
|
||||
:class="`grey ${$vuetify.theme.dark ? 'darken-4' : 'lighten-4'}`"
|
||||
style="position: sticky; top: 0; z-index: 10; width: 99%"
|
||||
style="position: sticky; top: 0; z-index: 6; width: 100%"
|
||||
>
|
||||
<router-link to="/" class="text-decoration-none">
|
||||
<v-img
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
</portal>
|
||||
|
||||
<portal to="nav">
|
||||
|
||||
<v-list v-if="stream" nav dense class="mt-0 pt-0">
|
||||
<v-list-item
|
||||
link
|
||||
@@ -33,9 +32,9 @@
|
||||
:stream-id="stream.id"
|
||||
/>
|
||||
|
||||
<views-display :views="views" />
|
||||
<views-display v-if="views.length !== 0" :views="views" />
|
||||
|
||||
<filters />
|
||||
<filters :props="objectProperties" :source-application="stream.commit.sourceApplication" />
|
||||
</portal>
|
||||
|
||||
<div style="height: 100vh; width: 100%; top: -64px; position: absolute">
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<div class="ml-2">
|
||||
<!-- <v-breadcrumbs large :items="navItems">
|
||||
<template #divider>
|
||||
<v-icon>mdi-chevron-right</v-icon>
|
||||
</template>
|
||||
</v-breadcrumbs> -->
|
||||
<div v-if="true">
|
||||
/
|
||||
<router-link
|
||||
|
||||
Reference in New Issue
Block a user