vue 3 监听form表单改变, 离开页面(路由切换, 组件切换)提示 (confirm提示)

521 阅读2分钟

效果

image.png

1 新建路由守卫

在router文件下新建一个router-guard的文件

image.png

    import router from '@/router';
    ​
    // 路由守卫 在localStorage 中存一个dirty属性
    router.beforeEach(async (to, _from, next) => {
        const isDirty = localStorage.getItem('isFormDirty');
        const tipRoutes = ["/conferenceData/index"] // 需要跳转的路由地址集合
        const tipText = '您有未保存的更改,确定要离开吗?'

        if (tipRoutes.include(_from.path)  && isDirty && !confirm(tipText)) {
            next(false); // 用户点击取消,阻止跳转
        } else {
            const isDirty = localStorage.removeItem('isFormDirty'); // 跳转之后移除这个标识
            next(); // 允许跳转
        }
    });

2 main.ts 导入 router-guards

import './router/router-guards';

3 具体业务代码 - 监听表单数据

数据发生改变的时候, 设置 localStorage 中 isFormDirty 字段, 表示表单已被修改

   watch(
       () => state.createFrom,
       () => {
           localStorage.setItem('isFormDirty', 'true')
       },
       { deep: true },
   );

几个注意小点: ( 容易出bug的地方 )

  1. 进入页面时, 重置状态

    onMounted(() => {
         // 进入页面重置状态
         localStorage.removeItem('isFormDirty')
     })
    
  2. 表单提交成功之后, 可能也需要清空表单. 这个节点是否需要自行判断 是否清空 isFormDirty字段

  3. 因为清空表单, 也会触发watch, 需要在nextTick 后再清空 代码如下:

    // 不这样写的话, 你的代码逻辑就是 提交成功 => 置空字段 => watch 检测你的置空 => 再次设置 isFormDirty 为 true. 导致设置失效
    nextTick(() => {
        localStorage.removeItem('isFormDirty')
    })
    
  4. 如果离开提示, 你点击取消. 但是菜单高亮还是变成了你点的菜单. 你可能就需要在 菜单文件中写一个 route.afterEach 的钩子去处理你的菜单高亮问题

    router.afterEach((to, from, failureReasons) => {
        // 如果有跳转原因, 则是取消跳转, 重置高亮菜单为之前的菜单
        if (failureReasons) {
            selectedKeys.value = [from.path];  // selectedKeys 是菜单v-model对应的值, 使用的库是ant-Menu
        }
    })
    

如果你的已修改表单离开, 单单路由切换需要提示, 问题到这里就结束了

88

4 组件切换提示

如果你还有组件切换, 已经编辑的表单也需要提示的话, 可以继续往下看
我的业务情况, 是在一个tab下, 有三个form, 每个tab切换之前需要提示(表单存在改动),如下图 image.png

  • 页面实现父组件内容 (精简版)
<template>
    <!-- 这里放弃v-model:activeKey="state.activeTab" 的写法, 用change事件手动触发 -->
    <a-tabs @change="tabChange" :activeKey="state.activeTab">
        <a-tab-pane key="tab1" tab="标题1"></a-tab-pane>
        <a-tab-pane key="tab2" tab="标题2"></a-tab-pane>
        <a-tab-pane key="tab2" tab="标题3"></a-tab-pane>
    </a-tabs>

    <component :is="state.activeTab"/>
</template>
<script lang="ts">


const tabChange = async (val: string) => {
    const isFormDirty = localStorage.getItem('isFormDirty')
    // 通过 isFormDirty 字段判断表单是否修改, 给出对应的提示
    if (!isFormDirty) {
        state.activeTab = val
        localStorage.removeItem('isFormDirty')
        return
    }

    const prompt = confirm('您有未保存的更改,确定要离开吗?')
    if (prompt) {
        state.activeTab = val
        localStorage.removeItem('isFormDirty')
    }
}

</script>

  • 至此, 已经编辑的表单, 组件之前的切换离开, 也会出现提示.