赞
踩
因为是在vue源码的基础上进行修改,所以,就没有复制代码在文章上,采取的是截图对比源码和我修改的代码片段。要麻烦你们自己手敲了。
场景:在费用配置列表中,点击每一项的配置,都会在页面容器内部打开一个新页面,所以新页面的路径一样,根据传的参数不同,面包屑和标签页标题动态改变
- http://localhost/feeManage/feeConfigDetail?id=4&metaTitle=3253的费用配置
-
- http://localhost/feeManage/feeConfigDetail?id=1&metaTitle=eww的费用配置
- // 方法1
- this.$router.push({
- path: '/feeManage/feeConfig/feeConfigDetail',
- query: {
- id: row.id,},
- })
- // 方法2
- <router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type">
- <span>点击跳转</span>
- </router-link>
若依vue前端能跳转到新页面路由的方法,我暂且知道这三种形式:
缺点:因为路由暴露在外面,会发生被误删或者修改错误的情况,造成严重缺陷。
优点:不用写路由配置的代码,可以直接进行路由跳转了。
同时还要注意,这是详细页面,不应该在左侧菜单栏出现,所以要隐藏
若依字典管理的动态路由配置(如果是想实现像若依字典这样的路由跳转效果,就可以直接参考若依的源码去做:
如果需要权限,需要自己写权限标识(很麻烦,,对接很累)
先是后端写上。。。
然后前端。。。
实现三级标题的路由怎么写?
- {
- path: '/feeManage',
- component: Layout, // 一级这个component: Layout必填,除非是不需要在页面容器里打开的页面
- hidden: true, // false:显示在侧边栏菜单
- redirect: 'noRedirect', // noRedirect:面包屑不可点击,不写这个,父级标题样式就和首页一样,黑字可点击跳转
- meta: { title: '费用管理'}, // 一级标题,写了才能显示在面包屑上
- children: [
- {
- path: '',
- component:{ render: (e) => e("router-view") }, // 如果你的'feeConfig'路径已经在系统菜单中设置过了,这里的path和component就写得和我一样就行,直接跳转三级路由
- hidden: true, // false:显示在侧边栏菜单
- redirect: 'noRedirect', // noRedirect:面包屑不可点击,不写这个,父级标题样式就和首页一样,黑字可点击跳转
- meta: { title: '费用配置'}, // 二级标题,写了才能显示在面包屑上
- // 如果你不需要二级的父级标题,那你就直接把第二个children的内容写在第一个children就行
- children: [
- {
- path: 'feeConfig/feeConfigDetail',
- component: () => import('@/views/feeManage/feeConfigDetail/index'),
- name: 'feeConfigDetail',
- meta: { title: '费用配置', activeMenu: '/feeManage/feeConfig' } // meta.title:三级标题,meta.activeMenu:侧边栏父级菜单高亮
- }
- ]
- }
- ]
- }
也可以这样写(这样写是建立在之前写的跳转路径不规范,如果不想改代码那么多,只能自己在路由这里改,就不用动业务代码里的跳转路径,当然我强迫症,我最后都改了)
- {
- path: '',
- component: Layout,
- hidden: true,
- redirect: 'noRedirect',
- meta: { title: '运营中心' },
- children: [
- {
- path: '/overseas-collocation',
- component:{ render: (e) => e("router-view") }, // 如果你的'merchant'路径已经在系统菜单中设置过了,这里的path和component就写得和我一样就行,直接跳转三级路由
- hidden: true, // false:显示在侧边栏菜单
- redirect: 'noRedirect', // noRedirect:面包屑不可点击,不写这个,父级标题样式就和首页一样,黑字可点击跳转
- meta: { title: '海外拼柜'}, // 二级标题,写了才能显示在面包屑上
- // 如果你不需要二级的父级标题,那你就直接把第二个children的内容写在第一个children就行
- children: [
- {
- path: 'detail/:id(\\d+)',
- component: () => import('@/views/operation-center/overseas-collocation/collocation-detail'),
- name: 'overseasCollocationDetail',
- meta: { title: '拼柜详情', activeMenu: '/operation-center/overseas-collocation/overseas-collocation' }
- }
- ]
- }
- ]
- }
- <router-link :to="'/overseas-collocation/detail/' + scope.row.id">
- <el-button type="text">查看</el-button>
- </router-link>
改后
配置完路由后,就要讲,如何动态设置路由path不同参数 在页面容器里打开新页面,面包屑和标签页标题根据参数动态改变
使用1方法创建好路由后,然后用$router.push设置传的参数,我们使用metaTitle来当页面标题
- this.$router.push({
- path: '/feeManage/feeConfigDetail',
- query: {
- id: row.id,
- metaTitle: row.chargeName + '的费用配置'
- },
- })
如果你只做到了这里,你就会发现,它确实跳转页面了,但是它是同一个页面进行了不同参数的刷新,然后页面的标题也没有动态改变,而是你之前菜单配置时写的标题,如图:
下面就需要改改若依的源码了:
1、先改面包屑
2、在页面容器中,打开新的标签页,改标签页标题(把要修改文件和修改内容框出来,有个明显的对比,知道改哪里)
最后在新页面取出参数
也是写完上面的内容以后,才发现有bug,路径一样,参数不一样的标签,去单击的时候,没有刷新内容,而是保留第一次点击的标签的页面。。。如图
原因:若依vue前端源码中用的<router-link>标签进行页面跳转,因为路径一样,参数不一样的页面本质上都是同一个vue,而这个vue已经加载出来就不会进行销毁重新加载了,所以我们要做的就是监听参数然后重新渲染,达到刷新页面的效果
在自己的跳转页面vue中监听路由参数:
ps: 找到更好的写法就又补充了一下,所以截图上有些不统一,记得看字看描述哈!
ruoyi 3.8.3
- <template>
- <el-breadcrumb class="app-breadcrumb" separator="/">
- <transition-group name="breadcrumb">
- <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
- <span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">
- <!-- {{ item.meta.title }} -->
- {{item.metaTitle ? item.metaTitle : item.meta.title }}
- </span>
- <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
- </el-breadcrumb-item>
- </transition-group>
- </el-breadcrumb>
- </template>
-
- <script>
- export default {
- data() {
- return {
- levelList: null
- }
- },
- watch: {
- $route(route) {
- // if you go to the redirect page, do not update the breadcrumbs
- if (route.path.startsWith('/redirect/')) {
- return
- }
- this.getBreadcrumb()
- }
- },
- created() {
- this.getBreadcrumb()
- },
- methods: {
- getBreadcrumb() {
- // only show routes with meta.title
- let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
- matched.forEach(element => {
- if(element.path == this.$route.path ) {
- if(this.$route.query.metaTitle) {
- element.metaTitle = this.$route.query.metaTitle
- }
- }
- });
- const first = matched[0]
-
- if (!this.isDashboard(first)) {
- matched = [{ path: '/index', meta: { title: '首页' }}].concat(matched)
- }
-
- this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
- },
- isDashboard(route) {
- const name = route && route.name
- if (!name) {
- return false
- }
- return name.trim() === 'Index'
- },
- handleLink(item) {
- const { redirect, path } = item
- if (redirect) {
- this.$router.push(redirect)
- return
- }
- this.$router.push(path)
- }
- }
- }
- </script>
-
- <style lang="scss" scoped>
- .app-breadcrumb.el-breadcrumb {
- display: inline-block;
- font-size: 14px;
- line-height: 50px;
- margin-left: 8px;
-
- .no-redirect {
- color: #97a8be;
- cursor: text;
- }
- }
- </style>
- <template>
- <div id="tags-view-container" class="tags-view-container">
- <scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
- <router-link
- v-for="tag in visitedViews"
- ref="tag"
- :key="tag.fullPath"
- :class="isActive(tag)?'active':''"
- :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
- tag="span"
- class="tags-view-item"
- :style="activeStyle(tag)"
- @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
- @contextmenu.prevent.native="openMenu(tag,$event)"
- >
- {{ tag.title }}
- <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
- </router-link>
- </scroll-pane>
- <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
- <li @click="refreshSelectedTag(selectedTag)"><i class="el-icon-refresh-right"></i> 刷新页面</li>
- <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> 关闭当前</li>
- <li @click="closeOthersTags"><i class="el-icon-circle-close"></i> 关闭其他</li>
- <li v-if="!isFirstView()" @click="closeLeftTags"><i class="el-icon-back"></i> 关闭左侧</li>
- <li v-if="!isLastView()" @click="closeRightTags"><i class="el-icon-right"></i> 关闭右侧</li>
- <li @click="closeAllTags(selectedTag)"><i class="el-icon-circle-close"></i> 全部关闭</li>
- </ul>
- </div>
- </template>
-
- <script>
- import ScrollPane from './ScrollPane'
- import path from 'path'
-
- export default {
- components: { ScrollPane },
- data() {
- return {
- visible: false,
- top: 0,
- left: 0,
- selectedTag: {},
- affixTags: []
- }
- },
- computed: {
- visitedViews() {
- return this.$store.state.tagsView.visitedViews
- },
- routes() {
- return this.$store.state.permission.routes
- },
- theme() {
- return this.$store.state.settings.theme;
- }
- },
- watch: {
- $route() {
- this.addTags()
- this.moveToCurrentTag()
- },
- visible(value) {
- if (value) {
- document.body.addEventListener('click', this.closeMenu)
- } else {
- document.body.removeEventListener('click', this.closeMenu)
- }
- }
- },
- mounted() {
- this.initTags()
- this.addTags()
- },
- methods: {
- isActive(route) {
- // return route.path === this.$route.path
- return route.fullPath === this.$route.fullPath
- },
- activeStyle(tag) {
- if (!this.isActive(tag)) return {};
- return {
- "background-color": this.theme,
- "border-color": this.theme
- };
- },
- isAffix(tag) {
- return tag.meta && tag.meta.affix
- },
- isFirstView() {
- try {
- return this.selectedTag.fullPath === this.visitedViews[1].fullPath || this.selectedTag.fullPath === '/index'
- } catch (err) {
- return false
- }
- },
- isLastView() {
- try {
- return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath
- } catch (err) {
- return false
- }
- },
- filterAffixTags(routes, basePath = '/') {
- let tags = []
- routes.forEach(route => {
- if (route.meta && route.meta.affix) {
- const tagPath = path.resolve(basePath, route.path)
- tags.push({
- fullPath: tagPath,
- path: tagPath,
- name: route.name,
- meta: { ...route.meta }
- })
- }
- if (route.children) {
- const tempTags = this.filterAffixTags(route.children, route.path)
- if (tempTags.length >= 1) {
- tags = [...tags, ...tempTags]
- }
- }
- })
- return tags
- },
- initTags() {
- const affixTags = this.affixTags = this.filterAffixTags(this.routes)
- for (const tag of affixTags) {
- // Must have tag name
- if (tag.name) {
- // this.$store.dispatch('tagsView/addVisitedView', tag)
- }
- }
- },
- addTags() {
- const { name } = this.$route
- if (name) {
- this.$store.dispatch('tagsView/addView', this.$route)
- }
- return false
- },
- moveToCurrentTag() {
- const tags = this.$refs.tag
- this.$nextTick(() => {
- for (const tag of tags) {
- if (tag.to.path === this.$route.path) {
- if(tag.to.query.metaTitle) {
- if(tag.to.query.metaTitle === this.$route.query.metaTitle) {
- this.$refs.scrollPane.moveToTarget(tag)
- // when query is different then update
- if (tag.to.fullPath !== this.$route.fullPath) {
- this.$store.dispatch('tagsView/updateVisitedView', this.$route)
- }
- }
- } else {
- this.$refs.scrollPane.moveToTarget(tag)
- // when query is different then update
- if (tag.to.fullPath !== this.$route.fullPath) {
- this.$store.dispatch('tagsView/updateVisitedView', this.$route)
- }
- }
- break
- }
- }
- })
- },
- refreshSelectedTag(view) {
- this.$tab.refreshPage(view);
- },
- closeSelectedTag(view) {
- this.$tab.closePage(view).then(({ visitedViews }) => {
- if (this.isActive(view)) {
- this.toLastView(visitedViews, view)
- }
- })
- },
- closeRightTags() {
- this.$tab.closeRightPage(this.selectedTag).then(visitedViews => {
- if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
- this.toLastView(visitedViews)
- }
- })
- },
- closeLeftTags() {
- this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => {
- if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
- this.toLastView(visitedViews)
- }
- })
- },
- closeOthersTags() {
- this.$router.push(this.selectedTag).catch(()=>{});
- this.$tab.closeOtherPage(this.selectedTag).then(() => {
- this.moveToCurrentTag()
- })
- },
- closeAllTags(view) {
- this.$tab.closeAllPage().then(({ visitedViews }) => {
- if (this.affixTags.some(tag => tag.path === this.$route.path)) {
- return
- }
- this.toLastView(visitedViews, view)
- })
- },
- toLastView(visitedViews, view) {
- const latestView = visitedViews.slice(-1)[0]
- if (latestView) {
- this.$router.push(latestView.fullPath)
- } else {
- // now the default is to redirect to the home page if there is no tags-view,
- // you can adjust it according to your needs.
- if (view.name === 'Dashboard') {
- // to reload home page
- this.$router.replace({ path: '/redirect' + view.fullPath })
- } else {
- this.$router.push('/')
- }
- }
- },
- openMenu(tag, e) {
- const menuMinWidth = 105
- const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
- const offsetWidth = this.$el.offsetWidth // container width
- const maxLeft = offsetWidth - menuMinWidth // left boundary
- const left = e.clientX - offsetLeft + 15 // 15: margin right
-
- if (left > maxLeft) {
- this.left = maxLeft
- } else {
- this.left = left
- }
-
- this.top = e.clientY
- this.visible = true
- this.selectedTag = tag
- },
- closeMenu() {
- this.visible = false
- },
- handleScroll() {
- this.closeMenu()
- }
- }
- }
- </script>
-
- <style lang="scss" scoped>
- .tags-view-container {
- height: 34px;
- width: 100%;
- background: #fff;
- border-bottom: 1px solid #d8dce5;
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
- .tags-view-wrapper {
- .tags-view-item {
- display: inline-block;
- position: relative;
- cursor: pointer;
- height: 26px;
- line-height: 26px;
- border: 1px solid #d8dce5;
- color: #495060;
- background: #fff;
- padding: 0 8px;
- font-size: 12px;
- margin-left: 5px;
- margin-top: 4px;
- &:first-of-type {
- margin-left: 15px;
- }
- &:last-of-type {
- margin-right: 15px;
- }
- &.active {
- background-color: #42b983;
- color: #fff;
- border-color: #42b983;
- &::before {
- content: '';
- background: #fff;
- display: inline-block;
- width: 8px;
- height: 8px;
- border-radius: 50%;
- position: relative;
- margin-right: 2px;
- }
- }
- }
- }
- .contextmenu {
- margin: 0;
- background: #fff;
- z-index: 3000;
- position: absolute;
- list-style-type: none;
- padding: 5px 0;
- border-radius: 4px;
- font-size: 12px;
- font-weight: 400;
- color: #333;
- box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
- li {
- margin: 0;
- padding: 7px 16px;
- cursor: pointer;
- &:hover {
- background: #eee;
- }
- }
- }
- }
- </style>
-
- <style lang="scss">
- //reset element css of el-icon-close
- .tags-view-wrapper {
- .tags-view-item {
- .el-icon-close {
- width: 16px;
- height: 16px;
- vertical-align: 2px;
- border-radius: 50%;
- text-align: center;
- transition: all .3s cubic-bezier(.645, .045, .355, 1);
- transform-origin: 100% 50%;
- &:before {
- transform: scale(.6);
- display: inline-block;
- vertical-align: -3px;
- }
- &:hover {
- background-color: #b4bccc;
- color: #fff;
- }
- }
- }
- }
- </style>
- const state = {
- visitedViews: [],
- cachedViews: []
- }
-
- const mutations = {
- // ADD_VISITED_VIEW: (state, view) => {
- // if (state.visitedViews.some(v => v.path === view.path)) return
- // state.visitedViews.push(
- // Object.assign({}, view, {
- // title: view.meta.title || 'no-name'
- // })
- // )
- // },
- ADD_VISITED_VIEW: (state, view) => {
- if (state.visitedViews.some(v => v.path === view.path)) {
- // 路径一样,因为参数不同,打开新的页面
- if(view.query && view.query.metaTitle) {
- let list = state.visitedViews.filter(v => v.query && v.query.metaTitle && v.query.metaTitle == view.query.metaTitle)
- if(list.length > 0) {
- return
- }
- } else {
- return
- }
- }
- state.visitedViews.push(
- Object.assign({}, view, {
- title: view.query && view.query.metaTitle ? view.query.metaTitle:(view.meta.title || 'no-name')
- })
- )
- },
- ADD_CACHED_VIEW: (state, view) => {
- if (state.cachedViews.includes(view.name)) return
- if (view.meta && !view.meta.noCache) {
- state.cachedViews.push(view.name)
- }
- },
-
- DEL_VISITED_VIEW: (state, view) => {
- for (const [i, v] of state.visitedViews.entries()) {
- if (v.path === view.path) {
- state.visitedViews.splice(i, 1)
- break
- }
- }
- },
- DEL_CACHED_VIEW: (state, view) => {
- const index = state.cachedViews.indexOf(view.name)
- index > -1 && state.cachedViews.splice(index, 1)
- },
-
- DEL_OTHERS_VISITED_VIEWS: (state, view) => {
- state.visitedViews = state.visitedViews.filter(v => {
- return v.meta.affix || v.path === view.path
- })
- },
- DEL_OTHERS_CACHED_VIEWS: (state, view) => {
- const index = state.cachedViews.indexOf(view.name)
- if (index > -1) {
- state.cachedViews = state.cachedViews.slice(index, index + 1)
- } else {
- state.cachedViews = []
- }
- },
-
- DEL_ALL_VISITED_VIEWS: state => {
- // keep affix tags
- const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
- state.visitedViews = affixTags
- },
- DEL_ALL_CACHED_VIEWS: state => {
- state.cachedViews = []
- },
-
- UPDATE_VISITED_VIEW: (state, view) => {
- for (let v of state.visitedViews) {
- if (v.path === view.path) {
- v = Object.assign(v, view)
- break
- }
- }
- },
-
- DEL_RIGHT_VIEWS: (state, view) => {
- const index = state.visitedViews.findIndex(v => v.path === view.path)
- if (index === -1) {
- return
- }
- state.visitedViews = state.visitedViews.filter((item, idx) => {
- if (idx <= index || (item.meta && item.meta.affix)) {
- return true
- }
- const i = state.cachedViews.indexOf(item.name)
- if (i > -1) {
- state.cachedViews.splice(i, 1)
- }
- return false
- })
- },
-
- DEL_LEFT_VIEWS: (state, view) => {
- const index = state.visitedViews.findIndex(v => v.path === view.path)
- if (index === -1) {
- return
- }
- state.visitedViews = state.visitedViews.filter((item, idx) => {
- if (idx >= index || (item.meta && item.meta.affix)) {
- return true
- }
- const i = state.cachedViews.indexOf(item.name)
- if (i > -1) {
- state.cachedViews.splice(i, 1)
- }
- return false
- })
- }
- }
-
- const actions = {
- addView({ dispatch }, view) {
- dispatch('addVisitedView', view)
- dispatch('addCachedView', view)
- },
- addVisitedView({ commit }, view) {
- commit('ADD_VISITED_VIEW', view)
- },
- addCachedView({ commit }, view) {
- commit('ADD_CACHED_VIEW', view)
- },
-
- delView({ dispatch, state }, view) {
- return new Promise(resolve => {
- dispatch('delVisitedView', view)
- dispatch('delCachedView', view)
- resolve({
- visitedViews: [...state.visitedViews],
- cachedViews: [...state.cachedViews]
- })
- })
- },
- delVisitedView({ commit, state }, view) {
- return new Promise(resolve => {
- commit('DEL_VISITED_VIEW', view)
- resolve([...state.visitedViews])
- })
- },
- delCachedView({ commit, state }, view) {
- return new Promise(resolve => {
- commit('DEL_CACHED_VIEW', view)
- resolve([...state.cachedViews])
- })
- },
-
- delOthersViews({ dispatch, state }, view) {
- return new Promise(resolve => {
- dispatch('delOthersVisitedViews', view)
- dispatch('delOthersCachedViews', view)
- resolve({
- visitedViews: [...state.visitedViews],
- cachedViews: [...state.cachedViews]
- })
- })
- },
- delOthersVisitedViews({ commit, state }, view) {
- return new Promise(resolve => {
- commit('DEL_OTHERS_VISITED_VIEWS', view)
- resolve([...state.visitedViews])
- })
- },
- delOthersCachedViews({ commit, state }, view) {
- return new Promise(resolve => {
- commit('DEL_OTHERS_CACHED_VIEWS', view)
- resolve([...state.cachedViews])
- })
- },
-
- delAllViews({ dispatch, state }, view) {
- return new Promise(resolve => {
- dispatch('delAllVisitedViews', view)
- dispatch('delAllCachedViews', view)
- resolve({
- visitedViews: [...state.visitedViews],
- cachedViews: [...state.cachedViews]
- })
- })
- },
- delAllVisitedViews({ commit, state }) {
- return new Promise(resolve => {
- commit('DEL_ALL_VISITED_VIEWS')
- resolve([...state.visitedViews])
- })
- },
- delAllCachedViews({ commit, state }) {
- return new Promise(resolve => {
- commit('DEL_ALL_CACHED_VIEWS')
- resolve([...state.cachedViews])
- })
- },
-
- updateVisitedView({ commit }, view) {
- commit('UPDATE_VISITED_VIEW', view)
- },
-
- delRightTags({ commit }, view) {
- return new Promise(resolve => {
- commit('DEL_RIGHT_VIEWS', view)
- resolve([...state.visitedViews])
- })
- },
-
- delLeftTags({ commit }, view) {
- return new Promise(resolve => {
- commit('DEL_LEFT_VIEWS', view)
- resolve([...state.visitedViews])
- })
- },
- }
-
- export default {
- namespaced: true,
- state,
- mutations,
- actions
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。