iView修改表單未保存離開頁面提示押桃,scrollIntoView頁面滾動動效

之前一直在用element-ui,這次我們改用iView框架导犹。

關(guān)于修改表單未保存離開頁面這里我們只考慮兩種情況唱凯,一種是直接關(guān)閉瀏覽器(直接關(guān)閉頁面),一種是路由跳轉(zhuǎn)(包含瀏覽器前進(jìn)后退)谎痢,以下是兩種提示頁面的截圖:

企業(yè)微信截圖_15954762187108.png

企業(yè)微信截圖_15954762307954.png
直接關(guān)閉瀏覽器的提示方式:

當(dāng)瀏覽器窗口關(guān)閉或者刷新時磕昼,會觸發(fā)beforeunload事件。當(dāng)前頁面不會直接關(guān)閉节猿,可以點擊確定按鈕關(guān)閉或刷新票从,也可以取消關(guān)閉或刷新。

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

事件使網(wǎng)頁能夠觸發(fā)一個確認(rèn)對話框,詢問用戶是否真的要離開該頁面峰鄙。如果用戶確認(rèn)浸间,瀏覽器將導(dǎo)航到新頁面,否則導(dǎo)航將會取消吟榴。

根據(jù)規(guī)范发框,要顯示確認(rèn)對話框,事件處理程序需要在事件上調(diào)用preventDefault()煤墙。

但是請注意梅惯,并非所有瀏覽器都支持此方法,而有些瀏覽器需要事件處理程序?qū)崿F(xiàn)兩個遺留方法中的一個作為代替:

  • 將字符串分配給事件的returnValue屬性
  • 從事件處理程序返回一個字符串仿野。

了解了這個方法铣减,我們在data里定義這三個屬性:

data () {
    return {
      isEdited: false, // 判斷表單是否被修改
      formData: {}, // 表單保存的數(shù)據(jù)
      initData: {} // 拷貝一份初始表單數(shù)據(jù),這里的數(shù)據(jù)不會改變
    }
  }

methods里定義beforeunload方法:

methods: {
    beforeunload (e) {
      // 表單被修改
      if (this.isEdited) {
        const confirmationMessage = '你確定離開此頁面嗎脚作?'
        e.returnValue = confirmationMessage
        return confirmationMessage
      }
    }
  }

在進(jìn)入頁面和銷毀頁面前綁定beforeunload方法:

mounted () {
    // 初始表單數(shù)據(jù)賦值
    this.initData = Object.assign({}, this.formData)
    // 攔截判斷是否離開當(dāng)前頁面
    window.addEventListener('beforeunload', this.beforeunload)
  },
beforeDestroy () {
    // 銷毀攔截判斷是否離開當(dāng)前頁面
    window.removeEventListener('beforeunload', this.beforeunload)
  }

我們判斷的邏輯是對比初始數(shù)據(jù)和修改表單后的數(shù)據(jù)葫哗,如果一致說明沒有改變表單數(shù)據(jù),否則就說明用戶編輯過表單球涛。通過watch去監(jiān)聽formData

watch: {
    formData: {
      deep: true,
      handler (val) {
        // 把兩個對象轉(zhuǎn)換成字符串比較
        if (JSON.stringify(this.initData) === JSON.stringify(val)) {
          this.isEdited = false
        } else {
          this.isEdited = true
        }
      }
    }
  }
vue路由跳轉(zhuǎn)的提示方式:

這需要你有另一個vue的頁面劣针,然后我們只需要在上面的基礎(chǔ)上加上beforeRouteLeave方法就行了。

beforeRouteLeave (to, from, next) {
    // 表單被修改
    if (this.isEdited) {
      this.$Modal.confirm({
        title: '系統(tǒng)監(jiān)測到你有未保存表單亿扁,是否直接離開捺典?',
        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('創(chuàng)建成功')
        } 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)
    // 攔截判斷是否離開當(dāng)前頁面
    window.addEventListener('beforeunload', this.beforeunload)
  },
  beforeDestroy () {
    // 銷毀攔截判斷是否離開當(dāng)前頁面
    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: '系統(tǒng)監(jiān)測到你有未保存表單从祝,是否直接離開襟己?',
        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>

這里我們還結(jié)合了Element.scrollIntoView()方法,當(dāng)表單驗證未通過時可以讓頁面直接滾動到報錯的那個位置牍陌。

scrollIntoViewOptions 可選
一個包含下列屬性的對象:
behavior 可選
定義動畫過渡效果擎浴, "auto"或 "smooth" 之一。默認(rèn)為 "auto"毒涧。
block 可選
定義垂直方向的對齊贮预, "start", "center", "end", 或 "nearest"之一。默認(rèn)為 "start"契讲。
inline 可選
定義水平方向的對齊仿吞, "start", "center", "end", 或 "nearest"之一。默認(rèn)為 "nearest"怀泊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茫藏,一起剝皮案震驚了整個濱河市误趴,隨后出現(xiàn)的幾起案子霹琼,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枣申,死亡現(xiàn)場離奇詭異售葡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)忠藤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門挟伙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人模孩,你說我怎么就攤上這事尖阔。” “怎么了榨咐?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵介却,是天一觀的道長。 經(jīng)常有香客問我块茁,道長齿坷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任数焊,我火速辦了婚禮永淌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘佩耳。我一直安慰自己遂蛀,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布干厚。 她就那樣靜靜地躺著答恶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萍诱。 梳的紋絲不亂的頭發(fā)上悬嗓,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音裕坊,去河邊找鬼包竹。 笑死,一個胖子當(dāng)著我的面吹牛籍凝,可吹牛的內(nèi)容都是我干的周瞎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼饵蒂,長吁一口氣:“原來是場噩夢啊……” “哼声诸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起退盯,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤彼乌,失蹤者是張志新(化名)和其女友劉穎泻肯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慰照,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡灶挟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了毒租。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稚铣。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖墅垮,靈堂內(nèi)的尸體忽然破棺而出惕医,到底是詐尸還是另有隱情,我是刑警寧澤算色,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布曹锨,位于F島的核電站,受9級特大地震影響剃允,放射性物質(zhì)發(fā)生泄漏沛简。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一斥废、第九天 我趴在偏房一處隱蔽的房頂上張望椒楣。 院中可真熱鬧,春花似錦牡肉、人聲如沸捧灰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毛俏。三九已至,卻和暖如春饲窿,著一層夾襖步出監(jiān)牢的瞬間煌寇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工逾雄, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留阀溶,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓鸦泳,卻偏偏與公主長得像银锻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子做鹰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355