iView修改表单未保存离开页面提示,scrollIntoView页面滚动动效

之前一直在用element-ui,这次我们改用iView框架。

关于修改表单未保存离开页面这里我们只考虑两种情况,一种是直接关闭浏览器(直接关闭页面),一种是路由跳转(包含浏览器前进后退),以下是两种提示页面的截图:

企业微信截图_15954762187108.png

企业微信截图_15954762307954.png
直接关闭浏览器的提示方式:

当浏览器窗口关闭或者刷新时,会触发beforeunload事件。当前页面不会直接关闭,可以点击确定按钮关闭或刷新,也可以取消关闭或刷新。

| Bubbles | No |
| Cancelable | Yes |
| Interface | Event |
| Event handler property | onbeforeunload |

事件使网页能够触发一个确认对话框,询问用户是否真的要离开该页面。如果用户确认,浏览器将导航到新页面,否则导航将会取消。

根据规范,要显示确认对话框,事件处理程序需要在事件上调用preventDefault()

但是请注意,并非所有浏览器都支持此方法,而有些浏览器需要事件处理程序实现两个遗留方法中的一个作为代替:

  • 将字符串分配给事件的returnValue属性
  • 从事件处理程序返回一个字符串。

了解了这个方法,我们在data里定义这三个属性:

data () {
    return {
      isEdited: false, // 判断表单是否被修改
      formData: {}, // 表单保存的数据
      initData: {} // 拷贝一份初始表单数据,这里的数据不会改变
    }
  }

methods里定义beforeunload方法:

methods: {
    beforeunload (e) {
      // 表单被修改
      if (this.isEdited) {
        const confirmationMessage = '你确定离开此页面吗?'
        e.returnValue = confirmationMessage
        return confirmationMessage
      }
    }
  }

在进入页面和销毁页面前绑定beforeunload方法:

mounted () {
    // 初始表单数据赋值
    this.initData = Object.assign({}, this.formData)
    // 拦截判断是否离开当前页面
    window.addEventListener('beforeunload', this.beforeunload)
  },
beforeDestroy () {
    // 销毁拦截判断是否离开当前页面
    window.removeEventListener('beforeunload', this.beforeunload)
  }

我们判断的逻辑是对比初始数据和修改表单后的数据,如果一致说明没有改变表单数据,否则就说明用户编辑过表单。通过watch去监听formData

watch: {
    formData: {
      deep: true,
      handler (val) {
        // 把两个对象转换成字符串比较
        if (JSON.stringify(this.initData) === JSON.stringify(val)) {
          this.isEdited = false
        } else {
          this.isEdited = true
        }
      }
    }
  }
vue路由跳转的提示方式:

这需要你有另一个vue的页面,然后我们只需要在上面的基础上加上beforeRouteLeave方法就行了。

beforeRouteLeave (to, from, next) {
    // 表单被修改
    if (this.isEdited) {
      this.$Modal.confirm({
        title: '系统监测到你有未保存表单,是否直接离开?',
        content: '',
        onOk: () => {
          next()
        },
        onCancel: () => {
          next(false)
        }
      })
    } else {
      next()
    }
  }

完整代码:

<template>
  <div class="main">
    <h3>基本信息</h3>
    <!-- 表单 -->
    <Form
      ref="form"
      :model="formData"
      :rules="formValidate"
      label-position="top"
    >
      <!-- 姓名 -->
      <Divider
        orientation="left"
        id="name"
      >
        <h5>姓名</h5>
      </Divider>
      <Row
        :gutter="20"
        class="main-block"
      >
        <Col span="12">
        <FormItem
          label="姓名"
          prop="name"
        >
          <Input
            placeholder="请输入姓名"
            v-model.trim="formData.name"
            clearable
          ></Input>
        </FormItem>
        </Col>
      </Row>
      <!-- 年龄 -->
      <Divider
        orientation="left"
        id="age"
      >
        <h5>年龄</h5>
      </Divider>
      <Row
        :gutter="20"
        class="main-block"
      >
        <Col span="12">
        <FormItem
          label="年龄"
          prop="age"
        >
          <Input
            placeholder="请输入年龄"
            v-model.trim="formData.age"
            clearable
          ></Input>
        </FormItem>
        </Col>
      </Row>
      <!-- 性别 -->
      <Divider
        orientation="left"
        id="gender"
      >
        <h5>性别</h5>
      </Divider>
      <Row
        :gutter="20"
        class="main-block"
      >
        <Col span="12">
        <FormItem
          label="性别"
          prop="gender"
        >
          <RadioGroup v-model="formData.gender">
            <Radio label="男"></Radio>
            <Radio label="女"></Radio>
          </RadioGroup>
        </FormItem>
        </Col>
      </Row>
      <!-- 称呼 -->
      <Divider
        orientation="left"
        id="tag"
      >
        <h5>称呼</h5>
      </Divider>
      <Row
        :gutter="20"
        class="main-block"
      >
        <Col span="12">
        <FormItem
          label="称呼"
          prop="tag"
        >
          <Select
            placeholder="请选择称呼"
            v-model="formData.tag"
            clearable
          >
            <Option
              v-for="(item, index) in tagList"
              :value="item.value"
              :key="index"
            >{{item.label}}</Option>
          </Select>
        </FormItem>
        </Col>
      </Row>
    </Form>
    <div class="btn-layer">
      <Button
        type="primary"
        style="margin-right:10px;"
        @click="onSubmit"
      >保存</Button>
      <Button @click="onCancel">重置</Button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data () {
    return {
      isEdited: false, // 判断表单是否被修改
      initData: {},
      formData: {
        name: '',
        age: '',
        gender: '男',
        tag: ''
      },
      formValidate: {
        name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
        age: [{ required: true, message: '请输入年龄', trigger: 'blur' }],
        gender: [{ required: true, message: '请输入性别', trigger: 'change' }],
        tag: [{ required: true, message: '请输入子会会议名称', trigger: 'change' }]
      },
      tagList: [
        { label: '先生', value: 'Mr.' },
        { label: '女士', value: 'Ms.' }
      ]
    }
  },
  methods: {
    onSubmit () {
      this.$refs.form.validate(valid => {
        if (valid) {
          this.isEdited = false
          this.$Message.success('创建成功')
        } else {
          this.$nextTick(() => {
            document.getElementsByClassName('ivu-form-item-error')[0].scrollIntoView({ behavior: 'smooth' })
          })
        }
      })
    },
    onCancel () {
      this.$refs.form.resetFields()
    },
    beforeunload (e) {
      if (this.isEdited) {
        const confirmationMessage = '你确定离开此页面吗?'
        e.returnValue = confirmationMessage
        return confirmationMessage
      }
    }
  },
  mounted () {
    this.initData = Object.assign({}, this.formData)
    // 拦截判断是否离开当前页面
    window.addEventListener('beforeunload', this.beforeunload)
  },
  beforeDestroy () {
    // 销毁拦截判断是否离开当前页面
    window.removeEventListener('beforeunload', this.beforeunload)
  },
  watch: {
    formData: {
      deep: true,
      handler (val) {
        if (JSON.stringify(this.initData) === JSON.stringify(val)) {
          this.isEdited = false
        } else {
          this.isEdited = true
        }
      }
    }
  },
  beforeRouteLeave (to, from, next) {
    if (this.isEdited) {
      this.$Modal.confirm({
        title: '系统监测到你有未保存表单,是否直接离开?',
        content: '',
        onOk: () => {
          next()
        },
        onCancel: () => {
          next(false)
        }
      })
    } else {
      next()
    }
  }
}
</script>

<style scoped>
.main {
  padding: 50px;
  max-width: 1200px;
  margin: 0 auto;
  border: 1px solid #ccc;
}
.main h3 {
  text-align: center;
  margin-bottom: 50px;
}
.main .btn-layer {
  margin: 30px 0;
  text-align: center;
}
.main .main-block {
  margin-bottom: 100px;
}
</style>

这里我们还结合了Element.scrollIntoView()方法,当表单验证未通过时可以让页面直接滚动到报错的那个位置。

scrollIntoViewOptions 可选
一个包含下列属性的对象:
behavior 可选
定义动画过渡效果, "auto"或 "smooth" 之一。默认为 "auto"。
block 可选
定义垂直方向的对齐, "start", "center", "end", 或 "nearest"之一。默认为 "start"。
inline 可选
定义水平方向的对齐, "start", "center", "end", 或 "nearest"之一。默认为 "nearest"。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342