Quasar QBreadcrumbs 组合式组件实现
# Quasar QBreadcrumbs 组合式组件实现
下面是一个完整的企业级QBreadcrumbs组合式组件实现,采用Vue 3组合式API、TypeScript,并遵循Quasar最佳实践。
```html
<template>
<div class="q-pa-md">
<!-- 动态面包屑组件 -->
<q-breadcrumbs class="q-mb-md" active-color="primary">
<q-breadcrumbs-el
v-for="(item, index) in breadcrumbItems"
:key="`breadcrumb-${index}`"
:icon="item.icon"
:label="item.label"
:to="item.to"
:disable="item.disable"
/>
<!-- 响应式折叠菜单 -->
<template v-if="collapsedItems.length > 0">
<q-breadcrumbs-el>
<q-btn
flat
dense
round
icon="more_horiz"
color="grey-7"
size="12px"
>
<q-menu>
<q-list dense>
<q-item
v-for="(item, index) in collapsedItems"
:key="`collapsed-${index}`"
clickable
v-close-popup
:to="item.to"
>
<q-item-section avatar v-if="item.icon">
<q-icon :name="item.icon" />
</q-item-section>
<q-item-section>
{{ item.label }}
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-breadcrumbs-el>
</template>
</q-breadcrumbs>
<!-- 页面内容 -->
<div class="page-content">
<h2 class="text-h4">{{ currentPageTitle }}</h2>
<p class="text-grey-8">当前路径: {{ $route.path }}</p>
<!-- 示例内容 -->
<div class="q-mt-lg">
<q-card flat bordered>
<q-card-section>
<div class="text-h6">页面内容区域</div>
<p>这是 {{ currentPageTitle }} 页面的示例内容。</p>
</q-card-section>
</q-card>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
// 定义面包屑项接口
interface BreadcrumbItem {
label: string;
icon?: string;
to?: string;
disable?: boolean;
}
const $q = useQuasar();
const route = useRoute();
const router = useRouter();
// 当前页面标题
const currentPageTitle = ref('产品详情');
// 根据路由动态生成面包屑
const breadcrumbItems = computed((): BreadcrumbItem[] => {
const items: BreadcrumbItem[] = [];
// 首页
items.push({
label: '首页',
icon: 'home',
to: '/'
});
// 根据路由路径添加面包屑项
const pathParts = route.path.split('/').filter(part => part);
let currentPath = '';
pathParts.forEach((part, index) => {
currentPath += `/${part}`;
// 模拟根据路径生成标签
const label = getLabelFromPath(part);
items.push({
label,
icon: getIconFromPath(part),
to: index === pathParts.length - 1 ? undefined : currentPath,
disable: index === pathParts.length - 1
});
});
return items;
});
// 响应式折叠 - 在小屏幕上折叠中间的面包屑项
const collapsedItems = computed((): BreadcrumbItem[] => {
if (!$q.screen.lt.md || breadcrumbItems.value.length <= 3) {
return [];
}
// 保留第一个和最后两个,中间的折叠
return breadcrumbItems.value.slice(1, -2);
});
// 根据路径获取显示标签
const getLabelFromPath = (path: string): string => {
const labelMap: Record<string, string> = {
'products': '产品管理',
'list': '产品列表',
'detail': '产品详情',
'users': '用户管理',
'settings': '系统设置',
'dashboard': '仪表板',
'analytics': '数据分析',
'reports': '报告中心'
};
return labelMap[path] || path.charAt(0).toUpperCase() + path.slice(1);
};
// 根据路径获取图标
const getIconFromPath = (path: string): string => {
const iconMap: Record<string, string> = {
'products': 'inventory_2',
'list': 'list',
'detail': 'info',
'users': 'people',
'settings': 'settings',
'dashboard': 'dashboard',
'analytics': 'analytics',
'reports': 'summarize'
};
return iconMap[path] || 'folder';
};
// 监听路由变化
watch(() => route.path, (newPath) => {
updateBreadcrumbs(newPath);
});
// 初始化面包屑
onMounted(() => {
updateBreadcrumbs(route.path);
});
const updateBreadcrumbs = (path: string): void => {
console.log('路由变化,更新面包屑:', path);
// 在实际应用中,这里可以根据路径更新页面标题等
const lastPart = path.split('/').filter(part => part).pop() || '';
currentPageTitle.value = getLabelFromPath(lastPart);
};
</script>
<style scoped lang="scss">
.page-content {
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
// 响应式调整
@media (max-width: 600px) {
.q-breadcrumbs {
font-size: 0.9rem;
:deep(.q-breadcrumbs__el) {
padding: 4px 6px;
}
}
}
</style>
```
## 使用示例
下面是如何在父组件中使用这个QBreadcrumbs组件的示例:
```html
<template>
<div>
<!-- 使用面包屑组件 -->
<app-breadcrumbs />
<!-- 页面导航示例 -->
<div class="q-pa-md">
<div class="text-h5 q-mb-md">页面导航</div>
<div class="q-gutter-md">
<q-btn
color="primary"
label="产品列表"
@click="$router.push('/products/list')"
/>
<q-btn
color="secondary"
label="产品详情"
@click="$router.push('/products/list/detail')"
/>
<q-btn
color="accent"
label="用户管理"
@click="$router.push('/users')"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import AppBreadcrumbs from './AppBreadcrumbs.vue';
import { useRouter } from 'vue-router';
const router = useRouter();
</script>
```
## 组合式函数版本
如果您希望将面包屑逻辑提取为可重用的组合式函数,可以创建以下文件:
```typescript
// composables/useBreadcrumbs.ts
import { computed, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
export interface BreadcrumbItem {
label: string;
icon?: string;
to?: string;
disable?: boolean;
}
export interface UseBreadcrumbsOptions {
homeLabel?: string;
homeIcon?: string;
homeTo?: string;
}
export function useBreadcrumbs(options?: UseBreadcrumbsOptions) {
const $q = useQuasar();
const route = useRoute();
const router = useRouter();
const {
homeLabel = '首页',
homeIcon = 'home',
homeTo = '/'
} = options || {};
const currentPageTitle = ref('');
const breadcrumbItems = computed((): BreadcrumbItem[] => {
const items: BreadcrumbItem[] = [];
// 首页
items.push({
label: homeLabel,
icon: homeIcon,
to: homeTo
});
// 根据路由路径添加面包屑项
const pathParts = route.path.split('/').filter(part => part);
let currentPath = '';
pathParts.forEach((part, index) => {
currentPath += `/${part}`;
const label = getLabelFromPath(part);
items.push({
label,
icon: getIconFromPath(part),
to: index === pathParts.length - 1 ? undefined : currentPath,
disable: index === pathParts.length - 1
});
});
return items;
});
const collapsedItems = computed((): BreadcrumbItem[] => {
if (!$q.screen.lt.md || breadcrumbItems.value.length <= 3) {
return [];
}
return breadcrumbItems.value.slice(1, -2);
});
const getLabelFromPath = (path: string): string => {
const labelMap: Record<string, string> = {
'products': '产品管理',
'list': '产品列表',
'detail': '产品详情',
'users': '用户管理',
'settings': '系统设置',
'dashboard': '仪表板',
'analytics': '数据分析',
'reports': '报告中心'
};
return labelMap[path] || path.charAt(0).toUpperCase() + path.slice(1);
};
const getIconFromPath = (path: string): string => {
const iconMap: Record<string, string> = {
'products': 'inventory_2',
'list': 'list',
'detail': 'info',
'users': 'people',
'settings': 'settings',
'dashboard': 'dashboard',
'analytics': 'analytics',
'reports': 'summarize'
};
return iconMap[path] || 'folder';
};
watch(() => route.path, (newPath) => {
updateBreadcrumbs(newPath);
});
const updateBreadcrumbs = (path: string): void => {
const lastPart = path.split('/').filter(part => part).pop() || '';
currentPageTitle.value = getLabelFromPath(lastPart);
};
return {
breadcrumbItems,
collapsedItems,
currentPageTitle,
updateBreadcrumbs
};
}
```
然后可以在组件中使用这个组合式函数:
```html
<template>
<div class="q-pa-md">
<q-breadcrumbs class="q-mb-md" active-color="primary">
<q-breadcrumbs-el
v-for="(item, index) in breadcrumbItems"
:key="`breadcrumb-${index}`"
:icon="item.icon"
:label="item.label"
:to="item.to"
:disable="item.disable"
/>
<template v-if="collapsedItems.length > 0">
<q-breadcrumbs-el>
<q-btn
flat
dense
round
icon="more_horiz"
color="grey-7"
size="12px"
>
<q-menu>
<q-list dense>
<q-item
v-for="(item, index) in collapsedItems"
:key="`collapsed-${index}`"
clickable
v-close-popup
:to="item.to"
>
<q-item-section avatar v-if="item.icon">
<q-icon :name="item.icon" />
</q-item-section>
<q-item-section>
{{ item.label }}
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-breadcrumbs-el>
</template>
</q-breadcrumbs>
<div class="page-content">
<h2 class="text-h4">{{ currentPageTitle }}</h2>
<!-- 页面内容 -->
</div>
</div>
</template>
<script setup lang="ts">
import { useBreadcrumbs } from '../composables/useBreadcrumbs';
const { breadcrumbItems, collapsedItems, currentPageTitle } = useBreadcrumbs({
homeLabel: '控制台',
homeIcon: 'dashboard',
homeTo: '/dashboard'
});
</script>
<style scoped lang="scss">
.page-content {
padding: 20px;
}
</style>
```
这个实现提供了完整的企业级QBreadcrumbs组件,具有以下特点:
1. **组合式API**:使用Vue 3的组合式API组织代码
2. **TypeScript支持**:完整的类型定义,避免使用any类型
3. **响应式设计**:根据屏幕尺寸自动折叠面包屑项
4. **路由集成**:与Vue Router深度集成
5. **可重用性**:提供了组合式函数版本,便于在不同组件中重用
6. **企业级特性**:支持自定义配置、国际化等扩展需求
您可以根据实际项目需求进一步扩展这个组件,例如添加权限控制、多语言支持等功能。