我写了一个含有步骤的弹窗

前言

先看效果:

步骤.gif

在执行步骤时,按钮的可用状态会改变,同时关闭弹窗操作也会出现二次提示。

按钮的提示文字和功能也会随步骤改变

问题分析

模拟一遍步骤,可以写出这些规则

(步骤1)上传文件时,不允许点击“下一步”;点击取消时,需要二次确认。=》“现在退出不会保存记录,是否确认退出?”
(步骤2)校验数据时,不允许点击按钮,点击X时,提示二次确认
(步骤3)写入数据时,不允许点击按钮=》写入完毕后,上一步按钮文字改变
复制代码

还有一些特殊情况的规则

(步骤1之前)“下一步”按钮禁用,为普通弹窗,关闭不需要二次确认
(从步骤2返回步骤1)“下一步”按钮禁用,此时关闭弹窗需要二次确认
复制代码

功能实现

根据上面的分析,可以使用一个变量表示当前进行到哪个步骤,然后根据这个变量改变按钮的文字、可用状态、以及该步骤应该执行的操作。

由于使用了element-ui组件,可以通过控制弹窗的visible属性来实现开关弹窗,还可以用自带的Message组件实现二次确认的弹窗。

除去表示进行到哪个步骤的变量外,还需要一些其他的。

      preBtn: '取消', // 两个按钮的文字
      nextBtn: '下一步',
      preBtnCheck: false, // 是否需要二次确认
      preBtnDisabled: false, // pre按钮的禁用状态
      nextBtnDisabled: false, // next按钮的禁用状态
      active: 0, // 步骤条的步数
复制代码

考虑到之后的步骤不一定固定为3步,设置了常量

const stepStart = 0; // 步骤开始
const stepEnd = 2; // 步骤结束
复制代码

前进后退

先来实现前进后退步骤的功能,给前进后退的按钮绑定upsetStep事件

    <el-button 
        :disabled="preBtnDisabled" 
        @click="upsetStep('pre')">
        {{preBtn}}
    </el-button>
    <el-button
        type="primary"
        :disabled="nextBtnDisabled"
        @click="upsetStep('next')">
        {{ nextBtn }}
    </el-button>
复制代码

upsetStep根据传入的参数对步骤变量进行加减,以及改变文字

      // 更新页面
      if (type === 'pre') {
        this.active--;
      } else {
        this.active++;
      }
      // 根据active改变按钮的文字
      if (this.active === stepStart) {
        this.preBtn = '取消';
        this.nextBtn = '下一步';
      } else {
        this.preBtn = '上一步';
      }
      if (this.active === stepEnd) {
        this.preBtn = '关闭窗口';
        this.nextBtn = '完成';
      }
复制代码

还需要在每次加减步骤时,检查一下是否是最初,最后的步骤,所以在函数开头加上了

    // 检查是否是最初、最后的步骤
      const {checked, closeType} = this.checkStep(type);
      if (checked) {
        this.closeDialog(closeType);
        return;
      }
复制代码
checkStep(type) {
      /**
       * this.active === stepStart&&type==='pre' 在第一页按下取消
       * this.active === stepEnd&&type==='next' 在最后一页按下完成
       * this.active === stepEnd&&type==='pre' 在最后一页按下关闭窗口
       */
      const checked =
        (this.active === stepStart && type === 'pre') ||
        (this.active === stepEnd && type === 'next') ||
        (this.active === stepEnd && type === 'pre');
      const closeType =
        (this.active === stepStart && type === 'pre') ||
        (this.active === stepEnd && type === 'pre')
          ? 'cancel'
          : this.active === stepEnd && type === 'next'
            ? 'confirm'
            : '';
      return {checked, closeType};
    }
复制代码

暂时还没想到好的优化方法,先这么写着吧,又不是不能用

按钮状态

然后来实现进行步骤时,改变按钮禁用状态。在上文“问题分析”模块可以得知,步骤一之前,由一个按钮触发步骤一,之后根据active的值触发步骤二、三。

于是在upsetStep函数最后加上

const handleInterfaces = ['', 'checkImportData', 'addImportData'];
const handleInterface = handleInterfaces[this.active];
      if (handleInterface.length > 0) {
        this[handleInterface]();
}
复制代码

这样,当点击“上传文件”按钮,执行uploadFile;当进行到步骤二、三时,会分别执行checkImportDataaddImportData。在这三个函数里,改变按钮的状态即可。

由于需要频繁改变按钮的状态,而实现这种操作实际上只是改变xxxBtnDisabled的值。于是写了个函数

changeBtnClickOperate(btnStatusName, type) {
      this[`${btnStatusName}`] = type;
    }
复制代码
    uploadFile() {
      // 模拟上传文件的接口
      // 将nextBtn设置为禁用
      this.changeBtnClickOperate('nextBtnDisabled', true);
      // 将preBtn设为需要二次确认
      this.changeBtnClickOperate('preBtnCheck', true);
      setTimeout(() => {
        this.changeBtnClickOperate('nextBtnDisabled', false);
      }, 2000);
    },
    checkImportData() {
      // 模拟校验数据的接口
      this.changeBtnClickOperate('preBtnDisabled', true);
      this.changeBtnClickOperate('nextBtnDisabled', true);
      setTimeout(() => {
        this.changeBtnClickOperate('preBtnDisabled', false);
        this.changeBtnClickOperate('nextBtnDisabled', false);
      }, 2000);
    },
    addImportData() {
      // 模拟写入数据的接口
      this.changeBtnClickOperate('nextBtnDisabled', true);
      this.changeBtnClickOperate('preBtnDisabled', true);
      setTimeout(() => {
        this.changeBtnClickOperate('nextBtnDisabled', false);
        this.changeBtnClickOperate('preBtnDisabled', false);
        this.changeBtnClickOperate('preBtnCheck', false);
      }, 2000);
    },
复制代码

最后,在步骤一没有点击“上传文件”按钮是无法进行下一步的,所以需要在upsetStep里加上

    // 根据active改变按钮的文字和可用状态
      if (this.active === stepStart) {
        this.preBtn = '取消';
        this.nextBtn = '下一步';
        this.changeBtnClickOperate('nextBtnDisabled', true);
      } else {
        this.preBtn = '上一步';
      }
复制代码

二次确认弹窗

最后,实现二次确认的弹窗。

    closeDialog(type) {
      // 检查是否需要弹出二次确认框
      if (this.preBtnCheck) {
        this.open(() => {
          this.dialogVisible = false;
        });
      } else {
        this.$emit(type);
        this.dialogVisible = false;
      }
    },
    handleClose() {
      // 这里处理点击按钮以外,关闭弹窗的情况
      if (this.preBtnCheck) {
        this.open(() => {
          this.dialogVisible = false;
        });
      } else {
        this.dialogVisible = false;
      }
    }
复制代码
    open(callback) {
      this.$confirm('现在取消不会保留任何更改,确认离开?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
        callback: (action) => {
          if (action === 'confirm') {
            callback();
          }
        },
      });
    }
复制代码

一些思考

在实现改变按钮状态的功能中,我频繁操作了按钮的disable。可不可以通过一个变量,控制三种状态呢?

根据涉及到改变按钮的函数uploadFileaddImportData以及checkImportData,进行分析

状态1 依赖函数uploadFile 
状态改变为:
        preBtnCheck:?->true
        nextBtnDisabled:?->true -> false
复制代码
状态2 依赖函数checkImportData
状态改变为:
        preBtnDisabled:?->true->false
        nextBtnDisabled:?->true->false
复制代码
状态3 依赖函数addImportData
状态改变为:
        preBtnCheck:?->false
        preBtnDisabled:?->true->false
        nextBtnDisabled:?->true->false
复制代码

有时候觉得好累,能用不就行了,为什么要帮产品把产品的事给干了。

不过业务仔暂时没时间想了,有缘再掘。

Guess you like

Origin juejin.im/post/7035081484503515143