vue3 tab切换函数回调刷新跳转页面

需求背景

页面miTab引用了两个组件A和B, 以tab切换方式显示,在tab页面A提交表单保存成功后跳转到tab页面B,并且tab页面B自动刷新表格列表能显示刚才保存成功的数据

出现的问题

跳转成功了,但是列表没刷新,排查后发现是没有在跳转后调用 tab B 的数据获取函数

解决方案

在页面miTab调用tab B组件的 getData 方法,前提是用 B页面提供注入功能来让父组件能够访问子组件的方法。

主要还得是在父组件中把逻辑处理好

关键代码

  • miTab.vue
html 复制代码
<template>
  <div>
    <el-tabs :model-value="activeName" @tab-click="handleClick">
      <el-tab-pane label="CAD-BOM" name="CAD-BOM">
        <CADBOMComponent @a-reuse-success="switchToaTab" />
      </el-tab-pane>
      <el-tab-pane label="a" name="a" >
        <aComponent ref="aComponent"/>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>

<script>
import { ref } from 'vue';
import CADBOMComponent from './CADBOMComponent.vue'; // 导入 CAD-BOM 组件
import aComponent from './aComponent.vue'; // 导入 a 组件

export default {
  name: 'part',
  components: {
    CADBOMComponent,
    aComponent
  },
  setup() {
    const activeName = ref('CAD-BOM');
    const aComponent = ref(null); // 用于引用 aComponent 实例



    const handleClick = (tab, event) => {
      activeName.value = tab.props.name;
    };

    const switchToaTab = () => {
      // 当收到子组件的 a-reuse-success 事件时,切换到 a tab
      activeName.value = 'a';
      if (aComponent.value) {
        aComponent.value.getData(); // 直接调用 aComponent 的 getData 方法
      }
    };


    return {
      activeName,
      handleClick,
      switchToaTab,
      aComponent
    };
  }
};
</script>
  • aTab
    表格表单代码以及其他逻辑处理太多,此处省略一万字...
html 复制代码
export default {
    name: 'aTab',
    setup(_,context) {
    }
    const emit = context.emit;
       const MBOMaddEdit = () => {
            const data = {
                partNo: MBOMForm.partNo,
                partRev: MBOMForm.partRev,
                name: MBOMForm.name,
                cName: MBOMForm.cName,
                material: MBOMForm.material,
                gauge: MBOMForm.gauge,
                massqty: MBOMForm.massqty,
                partType: MBOMForm.partType
            }
            addMBom(data).then(res => {
                if (res.code !== 500) {
                    ElMessage.success('恭喜您,添加成功!')
                    //通知父组件切换到 MBOM tab
                    emit('mbom-reuse-success');
                } else {
                    ElMessage.error(res.msg)
                }
            })
 }
  
  • bTab
html 复制代码
<template>
    <div class="part">
        <div class="common-query">
            <div class="common-query-demo">
                <span>零部件名称</span>
                <el-input placeholder="请输入零部件名称" v-model="query.name"></el-input>
            </div>
            <div class="common-query-demo">
                <el-button icon="el-icon-search" @click="handleQuery">查询</el-button>
                <el-button icon="el-icon-refresh" @click="resetQuery"
                    style="margin-left: 20px !important;">重置</el-button>
            </div>
        </div>
        <div class="line-body">
            <el-button type="primary" class="handle-btn" icon="el-icon-circle-plus-outline"
                @click="addEdit">新建</el-button>
            <el-button type="primary" class="handle-btn" icon="el-icon-delete" @click="deleteBatch">批量删除</el-button>

            <el-table :data="tableData" :max-height="tableHeight" @selection-change="handleSelectionChange" stripe
                border>
                <el-table-column type="selection" width="55" fixed></el-table-column>
                <el-table-column type="index" label="序号" width="50"></el-table-column>
                <el-table-column prop="name" label="名称" sortable min-width="100"></el-table-column>
                <el-table-column prop="cName" label="中文名称" min-width="100"></el-table-column>
                <el-table-column prop="partNo" label="数模号 " min-width="100"></el-table-column>
                <el-table-column prop="partRev" label="版本号" min-width="100"></el-table-column>
                <el-table-column prop="material" label="材料" min-width="100"></el-table-column>
                <el-table-column prop="gauge" label="规格" min-width="100"></el-table-column>
                <el-table-column prop="massqty" label="重量" min-width="100"></el-table-column>
                <el-table-column prop="partType" label="数据类型" min-width="100"></el-table-column>
                <el-table-column prop="status" label="状态" min-width="100">
                    <template #default="scope">
                        {{ scope.row.isLock == 0 ? '未锁定' : '已锁定' }}
                    </template>
                </el-table-column>

                <el-table-column fixed="right" label="操作" width="400">
                    <template #default="scope">
                        <el-button type="primary" icon="el-icon-view" @click="showTree(scope.row)">树形结构</el-button>
                        <el-button type="primary" icon="el-icon-view" @click="MBOMMux(scope.row)">MBOM复用</el-button>
                        <!-- 下载 -->
                        <el-button type="primary" icon="el-icon-download" @click="download(scope.row)">下载文件</el-button>
                        <i class="el-icon-delete" @click="deletePart(scope.row)"></i>
                    </template>
                </el-table-column>
            </el-table>

            <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
                :current-page="query.pageNum" :page-sizes="[10, 15, 20, 25, 30]" :page-size="query.pageSize"
                layout="total, sizes, prev, pager, next, jumper" :total="total">
            </el-pagination>

            <el-dialog :title="title" v-model="drawer" width="600px">
                <el-form :model="partForm" :rules="rules" ref="partFormsss" label-width="120px" class="demo-partForm">
                    <el-form-item label="数模号:" prop="partNo">
                        <el-input v-model="partForm.partNo" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="版本号:" prop="partRev">
                        <el-input v-model="partForm.partRev" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="名称:" prop="name">
                        <el-input v-model="partForm.name" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="上级节点:" v-if="partForm.parentName">
                        <el-input v-model="partForm.parentName" disabled></el-input>
                    </el-form-item>
                    <el-form-item label="中文名称:" prop="cName">
                        <el-input v-model="partForm.cName" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="材料:" prop="material">
                        <el-input v-model="partForm.material" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="规格:" prop="gauge">
                        <el-input v-model="partForm.gauge" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="重量:" prop="massqty">
                        <el-input v-model="partForm.massqty" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="数据类型:" prop="partType">
                        <el-input v-model="partForm.partType" clearable></el-input>
                    </el-form-item>
                </el-form>
                <template #footer>
                    <span class="dialog-footer">
                        <el-button @click="cancelEdit">取 消</el-button>
                        <el-button type="primary" @click="saveEdit">确 定</el-button>
                    </span>
                </template>
            </el-dialog>
        </div>

    </div>
</template>

<script>
import { reactive, ref, toRefs, unref, onMounted, nextTick, onUnmounted } from 'vue'
import { add, remove, listByPage, listAll, downloadFile } from '@api/pdm/part'
import { addMBom, getMBomDdata } from '@api/pdm/mbom'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router'
import axios from 'axios';


export default {
    name: 'bTab',
    setup() {
        // 定义的变量
        const state = reactive({
            query: {
                pageNum: 1,
                pageSize: 10,
                name: ''
            },
            statusList: [],
            title: '',
            drawer: false,  // 新建对话框
            handleId: '',  // 编辑id
            // 表数据
            tableData: [],
            total: 0,
            selectList: [],
            multipleSelection: []
        })
        const router = useRouter()

        // 通过计算表格距离页面底部的距离来实现表格高自适应
        const tableHeight = ref(window.innerHeight - 342)


        const partForm = reactive({
            partNo: '',
            partRev: '',
            name: '',
            cName: '',
            material: '',
            gauge: '',
            massqty: '',
            partType: '',
            parentId: '',
            parentName: ''
        })

        const partFormsss = ref(null)

        const rules = {
            name: [{ required: true, message: "此处为必填项" }],
            partNo: [{ required: true, message: "此处为必填项" }],
            partRev: [{ required: true, message: "此处为必填项" }]
        }



        //零部件表格
        function getData() {
            getMBomDdata(state.query).then(res => {
                state.total = res.data.total
                state.tableData = res.data.list
            })
        }



        // 新建零部件
        const addEdit = () => {
            state.drawer = true
            partForm.parentName = ''
            state.title = '添加零部件'
            partForm.parentId = ''
            partForm.partNo = ''
            partForm.partRev = ''
            partForm.name = ''
            partForm.cName = ''
            partForm.material = ''
            partForm.gauge = ''
            partForm.massqty = ''
            partForm.partType = ''
        }

        //展示零部件
        const showTree = (row) => {
            // state.title = '编辑零部件'
            // state.drawer = true
            // state.btnShow = true

            // state.handleId = row.id
            // partForm.partNo = row.partNo
            // partForm.partRev = row.partRev
            // partForm.name = row.name
            // partForm.cName = row.cName
            // partForm.material = row.cName
            // partForm.gauge = row.gauge
            // partForm.massqty = row.massqty
            // partForm.partType = row.partType

            // router.push({path: '/pdm/property?partId='+row.id})

            router.push({
                path: '/pdm/property',
                query: {
                    id: row.id
                }
            })
        }


        //断点续传
        // const download = async (row) => {
        //     const fileName = '11.png'; // 文件名
        //     const chunkSize = 1024 * 1024; // 分块大小,例如1MB
        //     let startByte = localStorage.getItem(`${fileName}-downloaded`) || 0;

        //     const config = {
        //         responseType: 'blob',
        //         headers: {
        //             'Range': `bytes=${startByte}-${startByte + chunkSize - 1}`,
        //         },
        //     };
        //     try {
        //         const response = await axios.get('/mes/file/download', config);
        //         // 直接处理响应,不再依赖于状态码检查
        //         const url = window.URL.createObjectURL(new Blob([response]));
        //         const link = document.createElement('a');
        //         link.href = url;
        //         link.download = fileName;
        //         document.body.appendChild(link);
        //         link.click();
        //         document.body.removeChild(link);

        //         // 更新已下载的字节数
        //         startByte += chunkSize;
        //         if (startByte < response.size) {
        //             localStorage.setItem(`${fileName}-downloaded`, startByte);
        //         } else {
        //             localStorage.removeItem(`${fileName}-downloaded`);
        //         }
        //     } catch (error) {
        //         console.error('Download error:', error);
        //     }
        // };




        const download = async (row) => {
            try {
                const data = {
                    id: row.id
                }
                downloadFile(data).then(response => {
                    if (response.code && response.code == 500) {
                        ElMessage.error(response.msg)
                    } else {
                        // 创建一个URL表示Blob对象
                        // const url = window.URL.createObjectURL(new Blob([response]));
                        const url = '/mes/pdm/cadDownloadPart?id=' + row.id;
                        // let fileName = response.headers['Content-Disposition'].split('filename=')[1];

                        // 创建隐藏的可下载链接
                        const link = document.createElement('a');
                        link.href = url
                        link.style.display = 'none';
                        // 将链接添加到DOM中,然后模拟点击下载
                        document.body.appendChild(link);
                        link.click();
                        // 清理工作,释放URL对象
                        window.URL.revokeObjectURL(url);
                        link.remove();
                    }
                })
            } catch (error) {
                // console.error('下载失败:', error);
                // alert('文件不存在或下载过程中出现问题!');
                ElMessage.error('下载失败')
            }
        };




        // 新建/编辑零部件
        const saveEdit = async () => {
            const form = unref(partFormsss)
            if (!form) return
            try {
                await form.validate()
                if (state.title == '添加零部件') {
                    addSavePart()
                }
                if (state.title == '编辑零部件') {
                    updateSavePart()
                }
                state.drawer = false
            } catch (error) {
                console.error(error)
                ElMessage.error('抱歉,您有必填项未填!')
            }
        }


        const addSavePart = () => {
            const data = {
                partNo: partForm.partNo,
                partRev: partForm.partRev,
                name: partForm.name,
                cName: partForm.cName,
                material: partForm.material,
                gauge: partForm.gauge,
                massqty: partForm.massqty,
                partType: partForm.partType,
            }
            add(data).then(res => {
                state.drawer = false
                ElMessage.success('恭喜您,添加成功!')
                getData()
            })
        }

        const updateSavePart = () => {
            const data = {
                id: state.handleId,
                partNo: partForm.partNo,
                partRev: partForm.partRev,
                name: partForm.name,
                cName: partForm.cName,
                material: partForm.material,
                gauge: partForm.gauge,
                massqty: partForm.massqty,
                partType: partForm.partType
            }
            update(data).then(res => {
                state.drawer = false
                getData()
                ElMessage.success('恭喜您,保存成功!')
            })
        }

        // 删除零部件
        const deletePart = (row) => {
            const ids = []
            ids.push(row.id)
            deleteProData(ids)
        }

        // 调用删除零部件接口
        const deleteProData = (data) => {
            remove(data).then(res => {
                getData(state.query)
                ElMessage.success('恭喜您,删除成功!')
            })
        }

        //批量删除零部件
        function deleteBatch() {
            const sels = state.multipleSelection
            if (sels.length != 0) {
                // 获取所有选中行的id组成的字符串,以逗号分隔
                const strIds = sels.map((item) => item.id).join()
                // split() 方法用于把一个字符串分割成字符串数组。
                const ids = strIds.split(',')
                deleteProData(ids)
                // ElMessage.success('您刪除了' + ids + '!')
            } else {
                ElMessage.error('您当前未选中!')
            }
        }

        // 新建取消
        const cancelEdit = () => {
            state.drawer = false
            setTimeout(() => {
                const form = unref(partFormsss)
                form.resetFields()
            }, 100)
        }

        const statusFormat = (row) => {
            for (let i = 0; i < state.statusList.length; i++) {
                if (row == state.statusList[i].dictValue) {
                    return state.statusList[i].dictLabel
                }
            }
        }

        // 批量删除选中
        function handleSelectionChange(val) {
            state.multipleSelection = val
        }


        function handleQuery() {
            queryEdit(state.query)
        }

        function resetQuery() {
            state.query.name = ''
        }

        // 查询
        function queryEdit(query) {
            getData(query)
            ElMessage.success('恭喜您,查询成功!')
        }




        // 分页页数
        function handleSizeChange(val) {
            state.query.pageSize = val
            queryEdit(state.query)
        }

        function handleCurrentChange(val) {
            state.query.pageNum = val
            queryEdit(state.query)
        }

        function getHeight() {
            tableHeight.value = window.innerHeight - 342
        }




        // 页面加载时调用函数
        onMounted(() => {
            // 注册监听器
            window.addEventListener('resize', getHeight)
            // 调用函数
            getHeight()
            getData()
        })

        onUnmounted(() => {
            // 注销监听器
            window.removeEventListener('resize', getHeight)
        })

    

        return {
            ...toRefs(state),
            tableHeight,
            rules,
            partFormsss,
            partForm,
            cancelEdit,
            saveEdit,
            handleSelectionChange,
            handleQuery,
            resetQuery,
            handleSizeChange,
            handleCurrentChange,
            showTree,
            addEdit,
            deletePart,
            deleteBatch,
            statusFormat,
            download,
            getData
        }
    }
}
</script>

<style lang="scss">
.part {
    width: 100%;
    height: 100%;

    .line-body {
        height: calc(100% - 120px);
        margin-top: 20px;
        box-shadow: $box-shadow;
        background: #fff;
        padding: 20px;

        .handle-btn {
            min-height: 32px;
            height: 32px;
            padding: 0 10px;
            margin-right: 10px;
        }

        .el-table {
            margin-top: 20px;

            td:last-child {
                .cell {
                    display: flex;
                    justify-content: space-between;
                    padding: 0 10px;
                }
            }

            .cell {
                padding: 0;

                .head-url {
                    height: 36px;
                    position: relative;

                    .el-image {
                        position: absolute;
                        top: 0;
                        right: 0;
                        bottom: 0;
                        left: 0;
                        margin: auto;
                        border-radius: 5px;
                    }
                }

                .el-tag {
                    height: 25px;
                    line-height: 25px;
                    padding: 0 7px;
                }
            }

            .el-icon-delete {
                cursor: pointer;
                color: #EB7A40;
                font-size: 22px;
            }

            .el-icon-delete:hover {
                opacity: 0.9;
            }
        }


    }
}
</style>

希望我的解决方案能帮助到遇到同样问题的同学,奥利给!

相关推荐
崔庆才丨静觅2 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了3 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅3 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅4 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊4 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax