0

我对 Vue 很陌生。我的应用程序有一个非常标准的布局,包括顶部导航、侧边导航、页脚和内容区域。内容区域分为两部分,左侧是树,右侧是选项卡式界面。我正在使用带有嵌套动态路由的 vue 路由器。

TreeAndTab.vue

import Vue from 'vue'
import VueRouter from 'vue-router'
/* import DefaultLayout from '../layout/Default.vue' */
/* import TreeAndTabLayout from '../layout/TreeAndTab.vue' */

Vue.use(VueRouter)

const routes = [
    {
        path: '/home',
        name: 'home',
        meta: { layout: 'default' },
        component: () => import('../pages/Home.vue')
    },
    {
        path: '/about',
        name: 'about',
        meta: { layout: 'default' },
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () => import('../pages/About.vue')
    },
    {
        path: '/dashboard',
        name: 'dashboard',
        meta: { layout: 'default' },
        component: () => import('../pages/dashboard/dashboard.vue')
    },
    {
        // Top level requirement goes to epic
        path: '/r/:epic_id?',
        //name: 'requirement',
        meta: { layout: 'default' },
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () => import('../pages/requirement/Requirement.vue'),
        children: [
            {
                path: '',
                name: 'epic',
                component: () => import('../pages/About.vue'),
                props: true,
                children: [
                    {
                        path: '/r/:epic_id/s/:story_id',
                        name: 'story',
                        component: () => import('../pages/Home.vue'),
                        props: true
                    }
                ]
            }
        ]
    }
]

const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
})

export default router

我有两种布局 - Default.vue 和 TreeAndTab.vue。当应用加载时,使用 Default.vue 并且页面进一步加载 TreeAndTab.vue 布局。

TreeAndTab.vue

<template>
    <tree-and-tab-layout
        :treeProps="treeProps"
        :tabProps="tabProps"
        :treeOptions="treeOptions"
    >
    </tree-and-tab-layout>
</template>

<script>
import TreeAndTabLayout from "../../layout/TreeAndTab.vue";
import RequirementService from "./RequirementService.js";

export default {
    components: {
        TreeAndTabLayout,
    },
    data: () => ({
        treeProps: {},
        tabProps: {
            tabs: [
                {
                    id: 1,
                    title: "epic",
                    route: { name: "epic" },
                },
                {
                    id: 2,
                    title: "story",
                    route: { name: "story" },
                },
                {
                    id: 3,
                    title: "mapping",
                    /* route: `/requirement/mapping/${this.$route.params.map_id}` */
                },
            ],
        },
        treeOptions: {
            propertyNames: {
                text: "title",
            },
        },
    }),
    methods: {
        getTabProps() {
            return {};
        },
    },
    created() {
        this.treeProps = RequirementService.getAllRequirementsForApp();
        //this.tabProps = this.getTabProps();
        this.treeProps.activeNode = [
            this.$route.params.epic_id || this.$route.params.story_id,
        ];
    },
};
</script>

需求.vue

<template>
    <tree-and-tab-layout
        :treeProps="treeProps"
        :tabProps="tabProps"
        :treeOptions="treeOptions"
    >
    </tree-and-tab-layout>
</template>

<script>
import TreeAndTabLayout from "../../layout/TreeAndTab.vue";
import RequirementService from "./RequirementService.js";

export default {
    components: {
        TreeAndTabLayout,
    },
    data: () => ({
        treeProps: {},
        tabProps: {
            tabs: [
                {
                    id: 1,
                    title: "epic",
                    route: { name: "epic" },
                },
                {
                    id: 2,
                    title: "story",
                    route: { name: "story" },
                },
                {
                    id: 3,
                    title: "mapping",
                    /* route: `/requirement/mapping/${this.$route.params.map_id}` */
                },
            ],
        },
        treeOptions: {
            propertyNames: {
                text: "title",
            },
        },
    }),
    methods: {
        getTabProps() {
            return {};
        },
    },
    created() {
        this.treeProps = RequirementService.getAllRequirementsForApp();
        //this.tabProps = this.getTabProps();
        this.treeProps.activeNode = [
            this.$route.params.epic_id || this.$route.params.story_id,
        ];
    },
};
</script>

我想要的流程如下:

  1. 当页面加载时,树中的第一个项目被选中。
  2. 当用户单击树中的父节点时,应选择右侧的第一个选项卡并加载适当的内容。它是路由器中的父路由。
  3. 当用户单击子负载时,应根据路由器加载第二个选项卡。

我看到树的行为正确,地址栏上显示了正确的路线。第一个选项卡的组件也正确加载。但是,当我单击叶节点时,即使正确创建了路由,选项卡也不会更新。选项卡既没有更改,也没有加载适当的组件。我尝试了各种选项,包括在选项卡中使用路由名称:to 等,但似乎没有任何效果。

任何帮助深表感谢。如果需要,我可以在 GitHub 上发布代码。

4

1 回答 1

0

最后我能够修复它。看起来选项卡上的路由设置不正确。这是我更改的内容:

  1. 将 tabProps 移至 compute() 以即时更新路线。
  2. 从子组件触发事件以更新被更新路由的父级捕获的路由。
  3. 我没有使用 this.$route 动态更新选项卡路由,因为如果在树上选择了子节点但用户切换到包含父级数据的第一个选项卡,我想保留第二个选项卡的状态。(我知道它令人困惑)。所以它就像一个文件资源管理器,其中第一个选项卡显示文件夹的详细信息,第二个选项卡显示该文件夹中所选子项的详细信息。

现在维护选项卡状态。

这是相关代码(不是最有效的,但它有效)。希望它可以帮助面临类似问题的人。

路由.js

/* eslint-disable no-unused-vars */
import Vue from 'vue'
import VueRouter from 'vue-router'
/* import DefaultLayout from '../layout/Default.vue' */
/* import TreeAndTabLayout from '../layout/TreeAndTab.vue' */

Vue.use(VueRouter)

const routes = [
    {
        path: '/home',
        name: 'home',
        meta: { layout: 'default' },
        component: () => import('../pages/Home.vue')
    },
    {
        path: '/about',
        name: 'about',
        meta: { layout: 'default' },
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () => import('../pages/About.vue')
    },
    {
        path: '/dashboard',
        name: 'dashboard',
        meta: { layout: 'default' },
        component: () => import('../pages/dashboard/dashboard.vue')
    },
    {
        // Top level requirement goes to epic
        path: '/plan',
        //name: 'requirement',
        meta: { layout: 'default' },
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () => import('../pages/requirement/Requirement.vue'),
        children: [
            {
                path: 'e/:epic_id',
                name: 'epic',
                component: () => import('../pages/About.vue'),
                props: true
            },
            {
                path: 'e/:epic_id/s/:story_id',
                name: 'story',
                component: () => import('../pages/Home.vue'),
                props: true
            }
        ]
    }
]

const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
})

export default router

页面/Requirement.vue

<template>
    <tree-and-tab-layout
        :treeProps="treeProps"
        :tabProps="tabProps"
        :treeOptions="treeOptions"
        v-on:activateTreeNode="handleTreeNodeActivate"
    >
    </tree-and-tab-layout>
</template>

<script>
//
import TreeAndTabLayout from "../../layout/TreeAndTab.vue";
import RequirementService from "./RequirementService.js";

export default {
    name: "RequirementPage",
    components: {
        TreeAndTabLayout,
    },
    data: () => ({
        base_path: "/plan",
        epic_id: "",
        story_id: "",
        epic_base_path: "/e/",
        story_base_path: "/s/",
        treeProps: {},
        treeOptions: {
            propertyNames: {
                text: "title",
            },
        },
    }),
    computed: {
        tabProps() {
            return {
                tabs: [
                    {
                        id: 1,
                        title: "Epic",
                        route:
                            this.base_path + this.epic_base_path + this.epic_id,
                    },
                    {
                        id: 2,
                        title: "Story",

                        route:
                            this.base_path +
                            this.epic_base_path +
                            this.epic_id +
                            this.story_base_path +
                            this.story_id,
                    },
                ],
            };
        },
    },
    methods: {
        handleTreeNodeActivate(child_id, parent_id) {
            this.story_id = child_id;
            this.epic_id = parent_id;
        },
    },
    created() {
        this.treeProps = RequirementService.getAllRequirementsForApp();
        // Does not work somehow. Handling it from the template
        //this.$on("activateTreeNode", this.handleTreeNodeActivate);
    },
};
</script>

TreeAndTab.vue

<template>
    <splitpanes>
        <pane size="30">
            <tree
                :data="treeProps.items"
                :options="treeOptions"
                ref="tree"
                @node:selected="onActive"
                @node:expanded="onExpand"
            />
        </pane>
        <pane size="70">
            <v-tabs v-model="activeTab" light>
                <!-- <v-tabs-slider></v-tabs-slider> -->
                <v-tab
                    v-for="tab in tabProps.tabs"
                    :key="tab.id"
                    :to="tab.route"
                    exact
                    >{{ tab.title }}</v-tab
                >
            </v-tabs>

            <v-card flat tile>
                <keep-alive>
                    <router-view />
                </keep-alive>
            </v-card>
        </pane>
    </splitpanes>
</template>

<script>
import { Splitpanes, Pane } from "splitpanes";
import LiquorTree from "liquor-tree";

import "splitpanes/dist/splitpanes.css";

export default {
    name: "TreeAndTab",
    components: {
        Splitpanes,
        Pane,
        tree: LiquorTree,
    },
    props: {
        treeProps: {
            type: Object,
            required: true,
        },
        tabProps: {
            type: Object,
            required: true,
        },

        treeOptions: {
            type: Object,
        },
    },
    data: () => ({
        newEpic: {
            id: "e_new",
            title: "New Epic",
            isFolder: true,
        },
        newStory: {
            id: "s_new",
            title: "New Story",
        },
        newCounter: 0,
        activeTab: null,
    }),
    mounted() {
        
        console.log("mounted Tree and Tab");
    },
    computed: {},

    methods: {
        onActive(node) {
            

            if (node.parent != null) {
                this.$emit("activateTreeNode", node.id, node.parent.id);
                this.$router.push({
                    name: "story",
                    params: { epic_id: node.parent.id, story_id: node.id },
                });

            } else {
                
                this.$emit("activateTreeNode", null, node.id);

                this.$router.push({
                    name: "epic",
                    params: { epic_id: node.id },
                });

                
            }
        },
        onExpand(node) {
            console.log("expand=", node);
        },
        
        getCurrentActiveNode() {
            return this.$refs.tree.selected()[0];
        },
    },
};
</script>

<style scoped>

</style> 

于 2021-05-21T05:16:22.667 回答