0
0
Fork 0

ref #6248 refactor breadcrumbs

This commit is contained in:
Jorge Penadés 2023-10-19 09:31:50 +02:00
parent eed1c68819
commit 5f12d64e73
7 changed files with 144 additions and 215 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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'),
},
],
},
], ],
}; };

View File

@ -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');
});
});

View File

@ -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');
});
});