需求:点击一个按钮,弹出一个模态框,这个模态框有两个tab,tab中是各种报警条件,这些报警条件是从数据库中动态取出的,数据库中数据变更后,这个界面也要变更,我们可以查看和编辑这些报警条件。底部“确定”按钮点击的时候,会同时将这两个tab中的内容都报错到数据库中去,数据录入要验证输入的格式。

  对于熟练的人来说,实现其实很简单,但是对于没有经验的人来说,如果按照官网给的那些简单实例来做,你会发现,出现一些奇怪的问题,诸如,文本框不能编辑内容,表单验证无效等。

  界面效果如下图所示:

  分析:用到的组件:el-dialog、el-tabs

   AlarmSet.vue代码:

<template>
  <div>
    <div class="alarm-set" v-loading="winLoading">
      <el-tabs v-model="activeName" type="border-card" v-if="alarmTmplData.length>0">
        <el-tab-pane label="制冷站" name="coldSet">
          <ColdSet
            ref="coldSet"
            :alarmTmplData="alarmTmplData"
            @onSubmit="coldSetSummit"
            :showAlarmSetWin.sync="showAlarmSetWin"
          ></ColdSet>
        </el-tab-pane>
        <el-tab-pane label="末端" name="endSet">
          <EndSet
            ref="endSet"
            :alarmTmplData="alarmTmplData"
            @onSubmit="endSetSummit"
            :showAlarmSetWin.sync="showAlarmSetWin"
          ></EndSet>
        </el-tab-pane>
      </el-tabs>
    </div>
    <div slot="footer" class="dialog-footer">
      <div style="display: inline-block">
        <el-button type="primary" @click="onSubmit" :loading="btnLoading">确 定</el-button>
        <el-button @click="isHide">取 消</el-button>
      </div>
    </div>
  </div>
</template>

<script>
import ColdSet from "./ColdSet";
import EndSet from "./EndSet";
import mixinsOption from "@/mixins/mixin-options";
import { alarmService } from "@/services/cold-station-service";
// import { alarmConditionData } from "@/mock/json.js";
export default {
  mixins: [mixinsOption],
  props: {
    showAlarmSetWin: {
      type: Boolean,
      default: false
    },
    alarmTmpl: {
      type: Object,
      default: {}
    }
  },
  components: {
    ColdSet,
    EndSet
  },
  data() {
    return {
      alarmConditionData: [], //报警条件数据
      activeName: "coldSet",
      alarmTmplData: [],
      coldConditionData: [], //冷站报警条件数据
      endConditionData: [], //末端报警条件数据
      isColdValid: false,
      isEndValid: false,
      btnLoading: false
    };
  },
  watch: {
    showAlarmSetWin: {
      handler(val) {
        console.log("showAlarmSetWin", val);
        if (val) {
          this.initData();
        }
      },
      immediate: true
    }
  },
  methods: {
    //初始化数据
    initData() {
      this.$store.commit("base/setWinLoading", true);
      console.log("initData");
      alarmService
        .getConditionList({
          groupNumber: this.groupNumber,
          projectNumber: this.projectNumber
        })
        .then(res => {
          if (res.code === 200) {
            this.alarmConditionData = res.data;
            this.createAlarmTmplData(res.data);
          }
          this.$store.commit("base/setWinLoading", false);
        });
    },
    //构造报警模板数据
    createAlarmTmplData(conditionData) {
      let res = [];
      this.alarmTmplData = this.alarmTmpl.data;
      if (this.alarmTmpl) {
        this.alarmTmplData.forEach(n => {
          // debugger;
          n.descr = eval(n.descr);
          let item = conditionData.find(m => m.tempId == n.id);
          if (item) {
            n["alarmLevel"] = item.alarmLevel;
            n["suggestion"] = item.suggestion;
            n["firstVal"] = item.firstVal;
            n["secondVal"] = item.secondVal;
            n["fourthVal"] = item.fourthVal;
            n["thirdVal"] = item.thirdVal;
            n["status"] = item.status;
          }
        });
      }
      // console.log("this.alarmTmplData :>> ", this.alarmTmplData);
    },
    //确定操作
    onSubmit() {
      this.$refs["coldSet"].onSubmit();
      this.$refs["endSet"].onSubmit();
      if (this.isColdValid && this.isEndValid) {
        this.btnLoading = true;
        let list = this.coldConditionData
          .concat(this.endConditionData)
          .map(m => {
            return {
              tempId: m.id,
              alarmLevel: m.alarmLevel,
              firstVal: m.firstVal,
              secondVal: m.secondVal,
              thirdVal: m.thirdVal,
              fourthVal: m.fourthVal,
              status: m.status,
              suggestion: m.suggestion
            };
          });

        alarmService
          .batchEdit({
            projectNumber: this.projectNumber,
            groupNumber: this.groupNumber,
            list: list
          })
          .then(res => {
            if (res.code === 200) {
              this.$message({
                message: "操作成功!",
                type: "success",
                duration: this.$baseConfig.messageDuration
              });
            }
            this.btnLoading = false;
          })
          .catch(() => {
            this.btnLoading = false;
          });
      }
    },
    coldSetSummit(val, isValid) {
      if (isValid) {
        this.isColdValid = isValid;
        this.coldConditionData = val;
      }
    },
    endSetSummit(val, isValid) {
      if (isValid) {
        this.isEndValid = isValid;
        this.endConditionData = val;
      }
    },
    //取消
    isHide() {
      this.$emit("update:showAlarmSetWin", false);
    }
  }
};
</script>

<style lang="scss" scoped>
.alarm-set {
  height: 600px;
  /deep/ .el-tabs--border-card {
    height: 100%;
  }
  /deep/ .el-tabs__content {
    height: calc(100% - 60px);
  }
}
</style>

关于表单验证:由于是动态生成的表单,所以不能按照官网提供的示例来做。

 el-form中不指定验证规则,而是在el-form-item中指定,如下:
:prop="`list.${rowIndex}.${fieldArr[2*index+index+1]}`" :rules="rules.numberRule"
表单数据ruleForm中药对数据进行初始化,否则会无法自动识别数据变化,建议采用深拷贝的形式
考虑到冷站和末端这两个组件中的代码可以复用,抽取公共js文件set-mixin.js
分析界面虽然是动态的,但是总共只有几种类型,分别是:1个文本框,2个文本框,没有文本框。他们都带有建议信息这一行,所以可以用几个v-if来区分。
import { alarmLevelOptions, fieldArr } from '@/enum/alarm-enum.js';
import Regexps from '@/utils/regexp.js';
import mixinsOption from '@/mixins/mixin-options';
export default {
  props: {
    alarmTmplData: {
      type: Array,
      default: () => {
        return [];
      },
    },
    showAlarmSetWin: {
      type: Boolean,
      default: false,
    },
  },
  mixins: [mixinsOption],
  data() {
    return {
      levelOptions: alarmLevelOptions(),
      ruleForm: {
        list: [],
      },
      rules: {
        numberRule: [
          {
            pattern: Regexps.commonNumber,
            message: '仅支持3位数和带1位小数',
            trigger: 'blur',
          },
        ],
        suggestion: [
          { max: 10, message: '长度不能超过10个字符', trigger: 'blur' },
        ],
      },
      fieldArr,
      tmplData: [],
    };
  },
  computed: {
    activeNames() {
      let activeNames = this.tmplData
        .filter((f) => f.descParamType != 0)
        .map((n) => {
          return n.id;
        });
      console.log('activeNames :>> ', activeNames);
      return activeNames;
    },
  },
  methods: {
    initData(type) {
      console.log('initData', type);
      if (this.alarmTmplData.length > 0) {
        this.tmplData = this.alarmTmplData.filter((n) => n.sys == type);
        this.ruleForm.list = JSON.parse(JSON.stringify(this.tmplData));
        // console.log('条件设置initData :>> ', this.ruleForm.list);
      }
    },
  },
};

ColdSet.vue代码:

<template>
  <div>
    <el-form :model="ruleForm" ref="ruleForm">
      <el-collapse v-model="activeNames">
        <el-collapse-item :name="item.id" v-for="(item,rowIndex) in ruleForm.list" :key="item.id">
          <template slot="title">
            <div class="header">
              <el-checkbox v-model="item.status" :true-label="0" :false-label="1">{{item.name}}</el-checkbox>
              <el-select v-model="item.alarmLevel" size="small">
                <el-option
                  v-for="item in levelOptions"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                ></el-option>
              </el-select>
            </div>
          </template>
          <div class="content vertical">
            <div class="row one" v-if="item.descParamType==1">
              <div class="item" v-for="(subItem,index) in item.descr" :key="index">
                <label>{{subItem}}</label>
                <el-form-item
                  label="大于"
                  :prop="`list.${rowIndex}.${fieldArr[index]}`"
                  :rules="rules.numberRule"
                >
                  <el-input v-model="item[`${fieldArr[index]}`]" size="small"></el-input>
                </el-form-item>
              </div>
            </div>
            <template v-if="item.descParamType==2">
              <div class="row two" v-for="(subItem,index) in item.descr" :key="index">
                <div class="item">
                  <label>{{subItem}}</label>
                  <el-form-item
                    label="大于"
                    :prop="`list.${rowIndex}.${fieldArr[2*index+index]}`"
                    :rules="rules.numberRule"
                  >
                    <el-input
                      v-model="item[`${fieldArr[2*index+index]}`]"
                      size="small"
                    ></el-input>
                  </el-form-item>
                  <el-form-item
                    label="或小于"
                    :prop="`list.${rowIndex}.${fieldArr[2*index+index+1]}`"
                    :rules="rules.numberRule"
                  >
                    <el-input
                      v-model="item[`${fieldArr[2*index+index+1]}`]"
                      size="small"
                    ></el-input>
                  </el-form-item>
                </div>
              </div>
            </template>

            <div class="row one">
              <el-form-item
                label="建议信息"
                :prop="`list.${rowIndex}.suggestion`"
                :rules="rules.suggestion"
              >
                <el-input
                  v-model="item.suggestion"
                  class="max-width"
                  size="small"
                  placeholder="请尽快处理"
                ></el-input>
              </el-form-item>
            </div>
          </div>
        </el-collapse-item>
      </el-collapse>
    </el-form>
  </div>
</template>

<script>
import { alarmLevelOptions, fieldArr } from "@/enum/alarm-enum.js";
import Regexps from "@/utils/regexp.js";
import { validateVal } from "@/utils/validate-utils.js";
import { alarmService } from "@/services/cold-station-service";
import setMixin from "./set-mixin";
export default {
  mixins: [setMixin],
  watch: {
    showAlarmSetWin: {
      handler(val) {
        console.log("showAlarmSetWin cold :>> ", val);
        if (val) {
          this.initData(1);
        }
         else{
           this.$refs[“ruleForm”].resetFields();
        }
      },
      immediate: true
    }
  },
  methods: {
    onSubmit() {
      console.log("冷站确定 :>> ");
      this.$refs["ruleForm"].validate(valid => {
        if (valid) {
          console.log("验证成功");
          this.$emit("onSubmit", this.ruleForm.list, true);
        }
      });
    }
  }
};
</script>

<style lang="scss" scoped>
@import "./set.scss";
</style>

EndSet.vue:

<template>
  <div>
    <el-form :model="ruleForm" ref="ruleForm">
      <el-collapse v-model="activeNames">
        <el-collapse-item :name="item.id" v-for="(item,rowIndex) in ruleForm.list" :key="item.id">
          <template slot="title">
            <div class="header">
              <el-checkbox v-model="item.status" :true-label="0" :false-label="1">{{item.name}}</el-checkbox>
              <el-select v-model="item.alarmLevel" size="small">
                <el-option
                  v-for="item in levelOptions"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                ></el-option>
              </el-select>
            </div>
          </template>
          <div class="content vertical">
            <div class="row two" v-if="item.descParamType==1">
              <div class="item" v-for="(subItem,index) in item.descr" :key="index">
                <label>{{subItem}}</label>
                <el-form-item
                  label="大于"
                  :prop="`list.${rowIndex}.${fieldArr[index]}`"
                  :rules="rules.numberRule"
                  v-if="index==0"
                >
                  <el-input v-model="item[fieldArr[index]]" size="small"></el-input>
                </el-form-item>
              </div>
            </div>
            <div class="row two" v-if="item.descParamType==2">
              <div class="item" v-for="(subItem,index) in item.descr" :key="index">
                <label>{{subItem}}</label>
                <el-form-item
                  label="大于"
                  :prop="`list.${rowIndex}.${fieldArr[2*index+index]}`"
                  :rules="rules.numberRule"
                >
                  <el-input v-model="item[fieldArr[2*index+index]]" size="small"></el-input>
                </el-form-item>
                <el-form-item
                  label="或小于"
                  :prop="`list.${rowIndex}.${fieldArr[2*index+index+1]}`"
                  :rules="rules.numberRule"
                >
                  <el-input v-model="item[fieldArr[2*index+index+1]]" size="small"></el-input>
                </el-form-item>
              </div>
            </div>
            <div class="row two" v-if="item.descParamType==3">
              <div class="item">
                <el-form-item
                  :label="subItem"
                  :prop="`list.${rowIndex}.${fieldArr[index]}`"
                  :rules="rules.numberRule"
                  v-for="(subItem,index) in item.descr"
                  :key="index"
                >
                  <el-input v-model="item[fieldArr[index]]" size="small"></el-input>
                </el-form-item>
              </div>
            </div>
            <template v-if="item.descParamType==4">
              <div class="row one" v-for="(subItem,index) in item.descr" :key="index">
                <div class="item">{{subItem}}</div>
                <div class="item">
                  <el-form-item
                    :label="index==0?'小于':'大于'"
                    :prop="`list.${rowIndex}.${fieldArr[index]}`"
                    :rules="rules.numberRule"
                  >
                    <el-input v-model="item[fieldArr[index]]" size="small"></el-input>
                  </el-form-item>
                </div>
              </div>
            </template>
            <!-- <div class="row multi-row" v-if="item.descParamType==4">
              multi-item       
            </div>-->
            <div class="row one">
              <el-form-item
                label="建议信息"
                :prop="`list.${rowIndex}.suggestion`"
                :rules="rules.suggestion"
              >
                <el-input
                  v-model="item.suggestion"
                  class="max-width"
                  size="small"
                  placeholder="请尽快处理"
                ></el-input>
              </el-form-item>
            </div>
          </div>
        </el-collapse-item>
      </el-collapse>
    </el-form>
  </div>
</template>

<script>
import setMixin from "./set-mixin";
import { alarmService } from "@/services/cold-station-service";
export default {
  mixins: [setMixin],
  watch: {
    showAlarmSetWin: {
      handler(val) {
        console.log("showAlarmSetWin end:>> ", val);
        if (val) {
          this.initData(2);
        }
         else{
           this.$refs[“ruleForm”].resetFields();
        }
      },
      immediate: true
    }
  },
  methods: {
    onSubmit() {
      console.log("末端确定 :>> ");
      this.$refs["ruleForm"].validate(valid => {
        if (valid) {
          console.log("验证成功");
          this.$emit("onSubmit", this.ruleForm.list, true);
        }
      });
    }
  }
};
</script>

<style lang="scss" scoped>
@import "./set.scss";
</style>

由于要两个tab中都验证通过时,才提交表单,所以两个表单都要验证,只有都验证通过时在提交表单,提交表单之前,要合并表单数据再统一提交。父组件调用子组件的方法 this.$refs[“coldSet”].onSubmit();

考虑到弹窗组件的性能问题,我们可以通过将显示标识以 :showAlarmSetWin.sync=”showAlarmSetWin”这样的形式传递给子组件,子组件监听showAlarmSetWin,当弹窗显示时,加载数据并初始化,并设置属性:immediate: true,让弹窗第一次执行时也加载数据。

当窗体隐藏时,重置表单:

this.$refs[“ruleForm”].resetFields();

为了防止同一时间多次点击操作按钮“确定”,可以给按钮加上loading

版权声明:本文为jiekzou原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/jiekzou/p/12988660.html