forked from verdnatura/salix-front
ref #6248 refactor breadcrumbs
This commit is contained in:
parent
eed1c68819
commit
5f12d64e73
|
@ -7,7 +7,7 @@ import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import PinnedModules from './PinnedModules.vue';
|
import PinnedModules from './PinnedModules.vue';
|
||||||
import UserPanel from 'components/UserPanel.vue';
|
import UserPanel from 'components/UserPanel.vue';
|
||||||
import VnBreadCrumbs from './common/VnBreadCrumbs.vue';
|
import VnBreadcrumbs from './common/VnBreadcrumbs.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
|
@ -58,11 +58,7 @@ const pinnedModulesRef = ref();
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<QToolbarTitle shrink class="text-weight-bold" v-if="$q.screen.gt.sm">
|
<VnBreadcrumbs v-if="$q.screen.gt.sm" />
|
||||||
{{ appName }}
|
|
||||||
<QBadge label="Beta" align="top" />
|
|
||||||
</QToolbarTitle>
|
|
||||||
<VnBreadCrumbs v-if="$q.screen.gt.xs" />
|
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<div id="searchbar" class="searchbar"></div>
|
<div id="searchbar" class="searchbar"></div>
|
||||||
<QSpace />
|
<QSpace />
|
||||||
|
@ -114,7 +110,7 @@ const pinnedModulesRef = ref();
|
||||||
<div id="actions-append"></div>
|
<div id="actions-append"></div>
|
||||||
</div>
|
</div>
|
||||||
</QToolbar>
|
</QToolbar>
|
||||||
<VnBreadCrumbs v-if="$q.screen.xs" class="q-ml-md" />
|
<VnBreadcrumbs v-if="$q.screen.lt.md" class="q-ml-md" />
|
||||||
</QHeader>
|
</QHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import { ref, watchEffect } from 'vue';
|
|
||||||
import { useQuasar } from 'quasar';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { useCamelCase } from 'src/composables/useCamelCase';
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const quasar = useQuasar();
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
let params = ref([]);
|
|
||||||
let childRoutes = ref([]);
|
|
||||||
let links = ref([]);
|
|
||||||
let root = ref([]);
|
|
||||||
let lastModel;
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
lastModel = undefined;
|
|
||||||
params.value = router.currentRoute.value.path
|
|
||||||
.split('/')
|
|
||||||
.filter((param) => param && isNaN(param));
|
|
||||||
childRoutes.value = router.options.routes[1].children;
|
|
||||||
root.value = params.value[0];
|
|
||||||
setLinks(params);
|
|
||||||
});
|
|
||||||
|
|
||||||
function setLinks(params) {
|
|
||||||
links.value.length = 0;
|
|
||||||
|
|
||||||
if (params.value.includes('dashboard')) return;
|
|
||||||
|
|
||||||
for (let index in params.value) {
|
|
||||||
const path =
|
|
||||||
index < params.value.length - 1
|
|
||||||
? '/' +
|
|
||||||
params.value.filter((item, itemIndex) => itemIndex <= index).join('/')
|
|
||||||
: undefined;
|
|
||||||
links.value.push(getLink(params.value[index], path));
|
|
||||||
}
|
|
||||||
links.value.forEach((link) => {
|
|
||||||
if (link) link.root = root;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function getLink(param, path) {
|
|
||||||
lastModel = getModel(param);
|
|
||||||
if (!lastModel) return;
|
|
||||||
|
|
||||||
const { icon, title } = lastModel.meta;
|
|
||||||
const breadcrumb = {
|
|
||||||
icon,
|
|
||||||
path,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (quasar.screen.gt.xs) {
|
|
||||||
breadcrumb.name = param;
|
|
||||||
breadcrumb.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
return breadcrumb;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getModel(param) {
|
|
||||||
if (!lastModel)
|
|
||||||
return childRoutes.value.find((child) => child.path.substring(1) == param);
|
|
||||||
|
|
||||||
let result = lastModel.children.find((child) => child.path == param);
|
|
||||||
if (result) return result;
|
|
||||||
|
|
||||||
for (const child of lastModel.children) {
|
|
||||||
if (child.children) {
|
|
||||||
result = child.children.find((item) => item.path === param);
|
|
||||||
if (result) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<QBreadcrumbs v-if="links.length && $q.screen.gt.xs" class="q-pa-xs">
|
|
||||||
<QBreadcrumbsEl
|
|
||||||
v-for="(link, index) of links"
|
|
||||||
:key="index"
|
|
||||||
:icon="link.icon"
|
|
||||||
:label="
|
|
||||||
t(`${useCamelCase(link.root)}.pageTitles.${useCamelCase(link.title)}`)
|
|
||||||
"
|
|
||||||
:to="link.path"
|
|
||||||
/>
|
|
||||||
</QBreadcrumbs>
|
|
||||||
<QBreadcrumbs v-else class="q-pa-xs">
|
|
||||||
<QBreadcrumbsEl
|
|
||||||
v-for="(link, index) of links"
|
|
||||||
:key="index"
|
|
||||||
:icon="link.icon"
|
|
||||||
:to="link.path"
|
|
||||||
/>
|
|
||||||
</QBreadcrumbs>
|
|
||||||
</template>
|
|
||||||
<style scoped></style>
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
<script setup>
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { ref, watchEffect } from 'vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useCamelCase } from 'src/composables/useCamelCase';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
let matched = ref([]);
|
||||||
|
let breadcrumbs = ref([]);
|
||||||
|
let root = ref(null);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
matched.value = router.currentRoute.value.matched.filter(
|
||||||
|
(matched) => Object.keys(matched.meta).length
|
||||||
|
);
|
||||||
|
breadcrumbs.value.length = 0;
|
||||||
|
|
||||||
|
if (matched.value[0].name != 'Dashboard') {
|
||||||
|
root.value = useCamelCase(matched.value[0].path.substring(1).toLowerCase());
|
||||||
|
|
||||||
|
for (let index in matched.value)
|
||||||
|
breadcrumbs.value.push(getBreadcrumb(matched.value[index]));
|
||||||
|
|
||||||
|
breadcrumbs.value[breadcrumbs.value.length - 1].path = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getBreadcrumb(param) {
|
||||||
|
const breadcrumb = {
|
||||||
|
icon: param.meta.icon,
|
||||||
|
path: param.path,
|
||||||
|
root: root.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (quasar.screen.gt.sm) {
|
||||||
|
breadcrumb.name = param.name;
|
||||||
|
breadcrumb.title = useCamelCase(param.meta.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
return breadcrumb;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QBreadcrumbs v-if="breadcrumbs.length && $q.screen.gt.sm" class="q-pa-xs">
|
||||||
|
<QBreadcrumbsEl
|
||||||
|
v-for="(breadcrumb, index) of breadcrumbs"
|
||||||
|
:key="index"
|
||||||
|
:icon="breadcrumb.icon"
|
||||||
|
:label="t(`${breadcrumb.root}.pageTitles.${breadcrumb.title}`)"
|
||||||
|
:to="breadcrumb.path"
|
||||||
|
/>
|
||||||
|
</QBreadcrumbs>
|
||||||
|
<QBreadcrumbs v-else class="q-pa-xs">
|
||||||
|
<QBreadcrumbsEl
|
||||||
|
v-for="(breadcrumb, index) of breadcrumbs"
|
||||||
|
:key="index"
|
||||||
|
:icon="breadcrumb.icon"
|
||||||
|
:to="breadcrumb.path"
|
||||||
|
/>
|
||||||
|
</QBreadcrumbs>
|
||||||
|
</template>
|
||||||
|
<style lang="scss">
|
||||||
|
.q-breadcrumbs {
|
||||||
|
&__el,
|
||||||
|
> div {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
.q-breadcrumbs {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__el:not(:first-child):not(:last-child) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,26 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
|
||||||
const route = useRoute();
|
|
||||||
const { t } = useI18n();
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
|
||||||
<QScrollArea class="fit">
|
|
||||||
<QSeparator />
|
|
||||||
<LeftMenu source="card" />
|
|
||||||
</QScrollArea>
|
|
||||||
</QDrawer>
|
|
||||||
|
|
||||||
<div><RouterView></RouterView></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<i18n>
|
|
||||||
es:
|
|
||||||
Search customer: Buscar cliente
|
|
||||||
You can search by customer id or name: Puedes buscar por id o nombre del cliente
|
|
||||||
</i18n>
|
|
|
@ -11,11 +11,11 @@ export default {
|
||||||
redirect: { name: 'WagonMain' },
|
redirect: { name: 'WagonMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['WagonList', 'WagonTypeList'],
|
main: ['WagonList', 'WagonTypeList'],
|
||||||
card: ['WagonEdit'],
|
card: [],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '/wagon',
|
||||||
name: 'WagonMain',
|
name: 'WagonMain',
|
||||||
component: () => import('src/pages/Wagon/WagonMain.vue'),
|
component: () => import('src/pages/Wagon/WagonMain.vue'),
|
||||||
redirect: { name: 'WagonList' },
|
redirect: { name: 'WagonList' },
|
||||||
|
@ -39,67 +39,7 @@ export default {
|
||||||
component: () => import('src/pages/Wagon/WagonCreate.vue'),
|
component: () => import('src/pages/Wagon/WagonCreate.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'type',
|
path: ':id/edit',
|
||||||
name: 'WagonTypeMain',
|
|
||||||
meta: {
|
|
||||||
title: 'typesList',
|
|
||||||
icon: 'view_list',
|
|
||||||
},
|
|
||||||
redirect: { name: 'WagonTypeList' },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'list',
|
|
||||||
name: 'WagonTypeList',
|
|
||||||
meta: {
|
|
||||||
title: 'typesList',
|
|
||||||
icon: 'view_list',
|
|
||||||
},
|
|
||||||
component: () =>
|
|
||||||
import('src/pages/Wagon/Type/WagonTypeList.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'create',
|
|
||||||
name: 'WagonTypeCreate',
|
|
||||||
meta: {
|
|
||||||
title: 'typeCreate',
|
|
||||||
icon: 'create',
|
|
||||||
},
|
|
||||||
component: () =>
|
|
||||||
import('src/pages/Wagon/Type/WagonTypeCreate.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ':id',
|
|
||||||
name: 'WagonTypeCard',
|
|
||||||
component: () =>
|
|
||||||
import('src/pages/Wagon/Type/Card/WagonTypeCard.vue'),
|
|
||||||
redirect: { name: 'WagonTypeEdit' },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'edit',
|
|
||||||
name: 'WagonTypeEdit',
|
|
||||||
meta: {
|
|
||||||
title: 'typeEdit',
|
|
||||||
icon: 'edit',
|
|
||||||
},
|
|
||||||
component: () =>
|
|
||||||
import(
|
|
||||||
'src/pages/Wagon/Type/WagonTypeCreate.vue'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'WagonCard',
|
|
||||||
path: ':id',
|
|
||||||
component: () => import('src/pages/Wagon/Card/WagonCard.vue'),
|
|
||||||
redirect: { name: 'WagonEdit' },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'edit',
|
|
||||||
name: 'WagonEdit',
|
name: 'WagonEdit',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'wagonEdit',
|
title: 'wagonEdit',
|
||||||
|
@ -109,5 +49,40 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/wagon/type',
|
||||||
|
name: 'WagonTypeMain',
|
||||||
|
component: () => import('src/pages/Wagon/WagonMain.vue'),
|
||||||
|
redirect: { name: 'WagonTypeList' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'list',
|
||||||
|
name: 'WagonTypeList',
|
||||||
|
meta: {
|
||||||
|
title: 'typesList',
|
||||||
|
icon: 'view_list',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Wagon/Type/WagonTypeList.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
name: 'WagonTypeCreate',
|
||||||
|
meta: {
|
||||||
|
title: 'typeCreate',
|
||||||
|
icon: 'create',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':id/edit',
|
||||||
|
name: 'WagonTypeEdit',
|
||||||
|
meta: {
|
||||||
|
title: 'typeEdit',
|
||||||
|
icon: 'edit',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
/// <reference types="cypress" />
|
|
||||||
describe('VnBreadCrumbs', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.login('developer');
|
|
||||||
cy.visit('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be breadcrumbs', () => {
|
|
||||||
cy.get('.q-breadcrumbs').should('not.exist');
|
|
||||||
});
|
|
||||||
it('should get the correct breadcrumbs', () => {
|
|
||||||
cy.get('[href="#/customer"]').click();
|
|
||||||
cy.get('.q-breadcrumbs .q-breadcrumbs--last').should('have.length', 1);
|
|
||||||
cy.get('.q-breadcrumbs .q-breadcrumbs--last').contains('List');
|
|
||||||
cy.get('.q-infinite-scroll > :nth-child(1)').click();
|
|
||||||
cy.get('.q-breadcrumbs .q-breadcrumbs__el').should('have.length', 2);
|
|
||||||
cy.get('.q-breadcrumbs .q-breadcrumbs__el').eq(1).contains('Summary');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
describe('VnBreadcrumbs', () => {
|
||||||
|
const firstCard = '.q-infinite-scroll > :nth-child(1)';
|
||||||
|
const lastBreadcrumb = '.q-breadcrumbs--last > .q-breadcrumbs__el';
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be breadcrumbs', () => {
|
||||||
|
cy.get('.q-breadcrumbs').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the correct breadcrumbs', () => {
|
||||||
|
cy.visit('#/customer/list');
|
||||||
|
cy.get('.q-breadcrumbs__el').should('have.length', 2);
|
||||||
|
|
||||||
|
cy.get(firstCard).click();
|
||||||
|
cy.get(`${lastBreadcrumb} > .q-icon`).should('have.text', 'launch');
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue