mirror of
https://github.com/fugary/simple-element-plus-template.git
synced 2025-11-12 14:27:49 +00:00
面包屑导航
This commit is contained in:
20
package-lock.json
generated
20
package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"dayjs": "^1.11.10",
|
||||
"element-plus": "^2.4.4",
|
||||
"lodash": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"vue": "^3.3.13",
|
||||
@@ -865,6 +866,14 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
|
||||
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -2469,6 +2478,17 @@
|
||||
"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
|
||||
"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
|
||||
},
|
||||
"node_modules/mockjs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mockjs/-/mockjs-1.1.0.tgz",
|
||||
"integrity": "sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==",
|
||||
"dependencies": {
|
||||
"commander": "*"
|
||||
},
|
||||
"bin": {
|
||||
"random": "bin/random"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"dayjs": "^1.11.10",
|
||||
"element-plus": "^2.4.4",
|
||||
"lodash": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"vue": "^3.3.13",
|
||||
|
||||
71
src/components/common-breadcrumb/index.vue
Normal file
71
src/components/common-breadcrumb/index.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
|
||||
import { useTabsViewStore } from '@/stores/TabsViewStore'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useMenuInfo, useMenuName } from '@/components/utils'
|
||||
|
||||
const globalConfigStore = useGlobalConfigStore()
|
||||
const tabsViewStore = useTabsViewStore()
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
const exists = []
|
||||
return route.matched.map(item => {
|
||||
const menuInfo = useMenuInfo(item)
|
||||
let icon = ''
|
||||
if (menuInfo && menuInfo.icon) {
|
||||
icon = menuInfo.icon
|
||||
} else if (item.meta && item.meta.icon) {
|
||||
icon = item.meta.icon
|
||||
}
|
||||
const result = {
|
||||
path: item.path,
|
||||
menuName: useMenuName(item),
|
||||
icon
|
||||
}
|
||||
console.info(item, menuInfo)
|
||||
return result
|
||||
}).filter(item => {
|
||||
const notExist = !exists.includes(item.menuName)
|
||||
if (notExist) {
|
||||
exists.push(item.menuName)
|
||||
}
|
||||
return notExist
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-breadcrumb
|
||||
v-if="globalConfigStore.isShowBreadcrumb"
|
||||
v-bind="$attrs"
|
||||
class="common-breadcrumb"
|
||||
>
|
||||
<el-breadcrumb-item
|
||||
v-for="item in breadcrumbs"
|
||||
:key="item.path"
|
||||
:to="{ path: item.path }"
|
||||
>
|
||||
<common-icon
|
||||
v-if="tabsViewStore.isShowTabIcon&&item.icon"
|
||||
:icon="item.icon"
|
||||
/>
|
||||
{{ item.menuName }}
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.common-breadcrumb {
|
||||
padding-left: 15px;
|
||||
padding-top: 10px;
|
||||
height: 30px;
|
||||
list-style: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.common-breadcrumb .el-icon {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
</style>
|
||||
@@ -27,7 +27,8 @@ const props = defineProps({
|
||||
default: '100px'
|
||||
},
|
||||
model: {
|
||||
type: Object
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
showButtons: {
|
||||
type: Boolean,
|
||||
@@ -40,10 +41,6 @@ const props = defineProps({
|
||||
showReset: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
submitForm: {
|
||||
type: Function,
|
||||
default () {}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -77,6 +74,8 @@ const rules = computed(() => {
|
||||
return ruleResult
|
||||
})
|
||||
|
||||
defineEmits(['submitForm'])
|
||||
|
||||
const form = ref()
|
||||
|
||||
</script>
|
||||
@@ -99,7 +98,7 @@ const form = ref()
|
||||
<el-button
|
||||
v-if="showSubmit"
|
||||
type="primary"
|
||||
@click="submitForm(form)"
|
||||
@click="$emit('submitForm', form)"
|
||||
>
|
||||
{{ $t('common.label.submit') }}
|
||||
</el-button>
|
||||
|
||||
@@ -113,8 +113,8 @@ const selectIcon = icon => {
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<RecycleScroller
|
||||
<el-main class="icon-area">
|
||||
<recycle-scroller
|
||||
v-slot="{ item }"
|
||||
class="scroller icon-list"
|
||||
:items="filterIcons"
|
||||
@@ -142,7 +142,7 @@ const selectIcon = icon => {
|
||||
</a>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</RecycleScroller>
|
||||
</recycle-scroller>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-dialog>
|
||||
@@ -164,4 +164,7 @@ const selectIcon = icon => {
|
||||
.el-radio {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.icon-area {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -28,7 +28,7 @@ const props = defineProps({
|
||||
required: true
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
type: [Number, String],
|
||||
required: false
|
||||
}
|
||||
})
|
||||
@@ -65,7 +65,7 @@ const dropdownClick = menuItem => {
|
||||
:key="menuItem.index||index"
|
||||
:class="menuCls"
|
||||
>
|
||||
{{ menuItem.splitText }}
|
||||
<slot name="split" />
|
||||
</div>
|
||||
<el-sub-menu
|
||||
v-else-if="isSubMenu"
|
||||
|
||||
@@ -32,7 +32,11 @@ const activeRoutePath = computed(() => {
|
||||
<common-menu-item
|
||||
:menu-item="menuItem"
|
||||
:index="index"
|
||||
/>
|
||||
>
|
||||
<template #split>
|
||||
<slot name="split" />
|
||||
</template>
|
||||
</common-menu-item>
|
||||
</template>
|
||||
<slot name="default" />
|
||||
</el-menu>
|
||||
|
||||
@@ -81,12 +81,12 @@ const onDropdownVisibleChange = (visible, tab) => {
|
||||
v-for="item in tabsViewStore.historyTabs"
|
||||
ref="tabItems"
|
||||
:key="item.path"
|
||||
:refresh-history-tab="refreshHistoryTab"
|
||||
:remove-history-tab="removeHistoryTab"
|
||||
:remove-other-history-tabs="removeOtherHistoryTabs"
|
||||
:remove-history-tabs="removeHistoryTabs"
|
||||
:on-dropdown-visible-change="onDropdownVisibleChange"
|
||||
:tab-item="item"
|
||||
@refresh-history-tab="refreshHistoryTab"
|
||||
@remove-other-history-tabs="removeOtherHistoryTabs"
|
||||
@remove-history-tabs="removeHistoryTabs"
|
||||
@on-dropdown-visible-change="onDropdownVisibleChange"
|
||||
@remove-history-tab="removeHistoryTab"
|
||||
/>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
@@ -12,29 +12,11 @@ const props = defineProps({
|
||||
tabItem: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
removeHistoryTab: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
removeOtherHistoryTabs: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
removeHistoryTabs: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
refreshHistoryTab: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
onDropdownVisibleChange: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
defineEmits(['removeHistoryTab', 'removeOtherHistoryTabs', 'removeHistoryTabs', 'refreshHistoryTab', 'onDropdownVisibleChange'])
|
||||
|
||||
const menuName = computed(() => {
|
||||
return useMenuName(props.tabItem)
|
||||
})
|
||||
@@ -69,7 +51,7 @@ defineExpose({
|
||||
:id="tabItem.path"
|
||||
ref="dropdownRef"
|
||||
trigger="contextmenu"
|
||||
@visible-change="onDropdownVisibleChange($event, tabItem)"
|
||||
@visible-change="$emit('onDropdownVisibleChange', $event, tabItem)"
|
||||
>
|
||||
<span class="custom-tabs-label">
|
||||
<common-icon
|
||||
@@ -81,35 +63,35 @@ defineExpose({
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@click="refreshHistoryTab(tabItem)"
|
||||
@click="$emit('refreshHistoryTab', tabItem)"
|
||||
>
|
||||
<common-icon icon="refresh" />
|
||||
{{ $t('common.label.refresh') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="tabsViewStore.hasCloseDropdown(tabItem, 'close')"
|
||||
@click="removeHistoryTab(tabItem.path)"
|
||||
@click="$emit('removeHistoryTab', tabItem.path)"
|
||||
>
|
||||
<common-icon icon="close" />
|
||||
{{ $t('common.label.close') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="tabsViewStore.hasCloseDropdown(tabItem, 'left')"
|
||||
@click="removeHistoryTabs(tabItem, 'left')"
|
||||
@click="$emit('removeHistoryTabs', tabItem, 'left')"
|
||||
>
|
||||
<common-icon icon="KeyboardDoubleArrowLeftFilled" />
|
||||
{{ $t('common.label.closeLeft') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="tabsViewStore.hasCloseDropdown(tabItem, 'right')"
|
||||
@click="removeHistoryTabs(tabItem, 'right')"
|
||||
@click="$emit('removeHistoryTabs', tabItem, 'right')"
|
||||
>
|
||||
<common-icon icon="KeyboardDoubleArrowRightFilled" />
|
||||
{{ $t('common.label.closeRight') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="tabsViewStore.hasCloseDropdown(tabItem, 'other')"
|
||||
@click="removeOtherHistoryTabs(tabItem)"
|
||||
@click="$emit('removeOtherHistoryTabs', tabItem)"
|
||||
>
|
||||
<common-icon icon="PlaylistRemoveFilled" />
|
||||
{{ $t('common.label.closeOther') }}
|
||||
|
||||
@@ -6,6 +6,7 @@ import CommonMenu from '@/components/common-menu/index.vue'
|
||||
import CommonMenuItem from '@/components/common-menu-item/index.vue'
|
||||
import CommonTabsView from '@/components/common-tabs-view/index.vue'
|
||||
import CommonTable from '@/components/common-table/index.vue'
|
||||
import CommonBreadcrumb from '@/components/common-breadcrumb/index.vue'
|
||||
|
||||
/**
|
||||
* 自定义通用组件自动注册
|
||||
@@ -23,5 +24,6 @@ export default {
|
||||
Vue.component('CommonMenuItem', CommonMenuItem)
|
||||
Vue.component('CommonTabsView', CommonTabsView)
|
||||
Vue.component('CommonTable', CommonTable)
|
||||
Vue.component('CommonBreadcrumb', CommonBreadcrumb)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,14 @@ const allMenus = computed(() => {
|
||||
mode="horizontal"
|
||||
:ellipsis="false"
|
||||
:menus="allMenus"
|
||||
/>
|
||||
>
|
||||
<template
|
||||
v-if="globalConfigStore.layoutMode === GlobalLayoutMode.LEFT && globalConfigStore.isShowBreadcrumb"
|
||||
#split
|
||||
>
|
||||
<common-breadcrumb :style="{'padding-left': '0','padding-top': '22px'}" />
|
||||
</template>
|
||||
</common-menu>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -39,6 +39,7 @@ common.label.tabMode = '多标签模式'
|
||||
common.label.cachedTabMode = '缓存标签页'
|
||||
common.label.showTabIcon = '标签图标'
|
||||
common.label.keywords = '关键字'
|
||||
common.label.breadcrumb = '面包屑导航'
|
||||
|
||||
//* =======================msg=====================//
|
||||
common.msg.nonNull = '{0}不能为空'
|
||||
|
||||
@@ -39,6 +39,7 @@ common.label.tabMode = 'Tabs Mode'
|
||||
common.label.cachedTabMode = 'Cache Tabs'
|
||||
common.label.showTabIcon = 'Tab Icon'
|
||||
common.label.keywords = 'Keywords'
|
||||
common.label.breadcrumb = 'Breadcrumb'
|
||||
|
||||
//* =======================msg=====================//
|
||||
common.msg.nonNull = '{0} is required.'
|
||||
|
||||
@@ -8,13 +8,18 @@ const router = createRouter({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
name: 'Home',
|
||||
component: HomeView,
|
||||
meta: {
|
||||
icon: 'HomeFilled',
|
||||
labelKey: 'common.label.index'
|
||||
},
|
||||
children: [{
|
||||
path: '',
|
||||
name: 'index',
|
||||
component: () => import('@/views/Index.vue'),
|
||||
meta: {
|
||||
icon: 'HomeFilled',
|
||||
labelKey: 'common.label.index'
|
||||
}
|
||||
}, {
|
||||
@@ -28,8 +33,12 @@ const router = createRouter({
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'notFound',
|
||||
component: () => import('@/views/404.vue')
|
||||
name: 'NotFound',
|
||||
component: () => import('@/views/404.vue'),
|
||||
meta: {
|
||||
icon: 'QuestionFilled',
|
||||
label: 'Not Found'
|
||||
}
|
||||
},
|
||||
...AdminRoutes,
|
||||
...ToolsRoutes
|
||||
|
||||
@@ -9,12 +9,14 @@ export const useGlobalConfigStore = defineStore('globalConfig', () => {
|
||||
const isDarkTheme = useDark()
|
||||
const isCollapseLeft = ref(false)
|
||||
const isShowSettings = ref(false)
|
||||
const isShowBreadcrumb = ref(true)
|
||||
const layoutMode = ref(GlobalLayoutMode.LEFT)
|
||||
return {
|
||||
currentLocale,
|
||||
isDarkTheme,
|
||||
isCollapseLeft,
|
||||
isShowSettings,
|
||||
isShowBreadcrumb,
|
||||
layoutMode,
|
||||
changeLocale (locale) {
|
||||
if (Object.values(GlobalLocales).includes(locale)) {
|
||||
|
||||
@@ -30,6 +30,12 @@ menuStore.loadBusinessMenus()
|
||||
<el-header>
|
||||
<top-nav />
|
||||
</el-header>
|
||||
<el-header
|
||||
v-if="globalConfigStore.layoutMode === GlobalLayoutMode.TOP && globalConfigStore.isShowBreadcrumb"
|
||||
class="tabs-header"
|
||||
>
|
||||
<common-breadcrumb />
|
||||
</el-header>
|
||||
<el-header
|
||||
v-if="tabsViewStore.isTabMode"
|
||||
class="tabs-header"
|
||||
|
||||
@@ -50,6 +50,15 @@ const options = [
|
||||
value: GlobalLayoutMode.TOP
|
||||
}]
|
||||
},
|
||||
{
|
||||
labelKey: 'common.label.breadcrumb',
|
||||
prop: 'isShowBreadcrumb',
|
||||
type: 'switch',
|
||||
model: globalConfigStore,
|
||||
change (val) {
|
||||
globalConfigStore.isShowBreadcrumb = val
|
||||
}
|
||||
},
|
||||
{
|
||||
labelKey: 'common.label.tabMode',
|
||||
prop: 'isTabMode',
|
||||
|
||||
@@ -9,7 +9,7 @@ const router = useRouter()
|
||||
<br>
|
||||
<el-link
|
||||
type="primary"
|
||||
@click="router.back()"
|
||||
@click="router.replace('/tables')"
|
||||
>
|
||||
{{ $t('common.label.back') }}
|
||||
</el-link>
|
||||
|
||||
Reference in New Issue
Block a user