form动态校验(动态增减表单项)

<template>
  <div style="flex: 1 1 0%;width: 100%;">
    <!-- 搜索条件 -->
    <el-form class="query-form" ref="form" :model="searchForm" label-width="60px">
      <el-row :gutter="24">
        <el-col :span="4">
          <el-form-item label="规则名称">
            <el-input v-model.trim="searchForm.name" placeholder="请输入规则名称" clearable></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="3">
          <el-form-item label="规则分类">
            <el-select
              v-model="searchForm.rule_type"
              style="width: 100%;"
              clearable
              placeholder="请选择"
              @clear="searchForm.rule_type = null"
            >
              <el-option
                v-for="item in ruleTypesOptions"
                :key="item.conf_key"
                :label="item.conf_value"
                :value="item.conf_key"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="3">
          <el-form-item label="状态">
            <el-select
              v-model="searchForm.status"
              style="width: 100%;"
              clearable
              placeholder="请选择"
              @clear="searchForm.status = null"
            >
              <el-option
                v-for="item in statusArr"
                :key="item.index"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="4">
          <el-button type="primary" @click="handleQuery">
            <i class="iconfont icon-btn">&#xe980;</i>
            查询
          </el-button>
          <el-button type="primary" @click="handleAdd">
            <i class="el-icon-circle-plus"></i>
            新增
          </el-button>
        </el-col>
      </el-row>
    </el-form>
    <!-- 内容部分 -->
    <el-container>
      <el-main>
        <div class="table-inner">
          <el-table
            ref="singleTable"
            v-loading="listLoading"
            element-loading-text="加载中..."
            :data="tableData"
            border
            show-overflow-tooltip
            highlight-current-row
            @current-change="handleCurrentCulumn"
            height="100%">
            <el-table-column
              label="序号"
              fixed
              width="60"
              align="center">
              <template slot-scope="scope">{{ scope.$index+1 }}</template>
            </el-table-column>
            <el-table-column
              label="操作"
              fixed
              width="90">
              <template slot-scope="scope">
                <el-button
                  type="text"
                  @click.stop="handleStatusChange(scope.row)">
                  {{ scope.row.status === 1 ? '禁用' : '启用' }}
                </el-button>
                <el-button type="text" @click.stop="handleDelete(scope.row, scope.$index)">删除</el-button>
              </template>
            </el-table-column>
            <el-table-column
              label="规则名称"
              prop="name">
            </el-table-column>
            <el-table-column
              label="规则分类">
              <template slot-scope="scope">
                <span>{{ scope.row.category_id | selectOptionFilter(that, ruleTypesOptions) }}</span>
              </template>
            </el-table-column>
            <el-table-column
              prop="status"
              label="状态"
              width="70">
              <template slot-scope="scope">
                <span>
                  <i class="status-circle" :class="scope.row.status ? 'el-button--success':'el-button--danger'"></i>
                  <span :class="scope.row.status ? 'status-success':'status-danger'">{{ scope.row.status | statusFilter }}</span>
                </span>
              </template>
            </el-table-column>
          </el-table>
        </div>
        <!-- 分页 -->
        <el-pagination
          class="person-pagination page"
          background
          :current-page="currentPage"
          :page-sizes="[20, 50, 100, 200]"
          :page-size="pageSize"
          layout="sizes, total, jumper, prev, pager, next"
          :total="total"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        ></el-pagination>
      </el-main>

      <el-aside width="60%" v-loading="editPartLoading">
        <m-card-title :level="1">告警规则
        <div slot="btn-group">
          <div style="border-top: none" v-if="isEditRuleFlag">
            <el-button type="primary" @click="onSaveRuleDetail" v-if="isEdit" >提交</el-button>

            <el-button type="primary" @click="onEditRuleDetail" v-else>编辑</el-button>
            <!--<el-button @click="onReset" v-if="isEdit">重置</el-button>-->
          </div>
        </div>
        </m-card-title>
        <div class="el-aside-form">
          <el-form ref="ruleForm" :rules="rules" :model="form" label-width="90px" v-loading="ruleDetailLoading">
            <el-row>
              <el-col :span="24">
                <el-form-item label="规则名称" prop="name">
                  <el-input
                    v-model.trim="form.name"
                    maxlength="140"
                    style="width: 100%;"
                    :disabled="isEditRuleFlag && !isEdit"
                    clearable
                    placeholder="不超过140个字符"
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row>
              <el-col :span="8">
                <el-form-item label="规则分类" prop="category_id">
                  <el-select
                    v-model="form.category_id"
                    style="width: 100%;"
                    placeholder="请选择"
                    :disabled="isEditRuleFlag && !isEdit"
                  >
                    <el-option
                      v-for="item in ruleTypesOptions"
                      :key="item.index"
                      :label="item.conf_value"
                      :value="item.conf_key"
                    />
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col :span="8" v-if="isEditRuleFlag">
                <el-form-item label="变更人">
                  <el-input
                    v-model.trim="form.updated_by"
                    maxlength="20"
                    style="width: 100%;"
                    disabled
                    clearable
                    placeholder="不超过20个字符"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="8" v-if="isEditRuleFlag">
                <el-form-item label="变更时间">
                  <el-input
                    v-model.trim="form.updated_time"
                    maxlength="20"
                    style="width: 100%;"
                    disabled
                    clearable
                    placeholder="不超过20个字符"
                  />
                </el-form-item>
              </el-col>
            </el-row>

            <el-row>
              <el-col :span="24">
                <el-form-item label="规则描述" prop="description">
                  <el-input
                    v-model.trim="form.description"
                    maxlength="600"
                    style="width: 100%;"
                    :disabled="isEditRuleFlag &&!isEdit"
                    clearable
                    type="textarea"
                    :autosize="{ minRows: 2, maxRows: 2}"
                    placeholder="请输入备注"
                  />
                </el-form-item>
              </el-col>
            </el-row>

            <el-row>
              <el-col :span="24">
                <el-form-item label="执行规则频率" prop="interval_time">
                  <el-input-number
                  style="width: 120px"
                  v-model="form.interval_time"
                  :disabled="isEditRuleFlag && !isEdit"
                  :min="1"
                  :max="10000"
                  label="描述文字">
                </el-input-number>
                <el-select
                  v-model="form.time_unit"
                  style="width: 120px"
                  placeholder="请选择"
                  :disabled="isEditRuleFlag && !isEdit"
                >
                  <el-option
                    v-for="item in timeUnitArr"
                    :key="item.index"
                    :label="item.conf_key"
                    :value="item.conf_value"
                  />
                </el-select>
                </el-form-item>
              </el-col>
            </el-row>

            <m-card-title>过滤条件</m-card-title>

            <el-row>
              <el-col :span="8">
                <el-form-item label="时间范围" prop="latest_hours">
                  <el-input-number
                    v-model="form.latest_hours"
                    :disabled="isEditRuleFlag && !isEdit"
                    :min="1"
                    :max="10000"
                    label="描述文字"
                    style="width: 120px">
                  </el-input-number>
                  小时
                </el-form-item>
              </el-col>
              <el-col :span="16">
                <el-form-item label="时间范围" prop="duration" >
                  <el-checkbox :disabled="isEditRuleFlag && !isEdit" v-model="form.timeAroundFlag"></el-checkbox>
                  <el-time-picker
                    v-show="form.timeAroundFlag"
                    is-range
                    :disabled="isEditRuleFlag && !isEdit"
                    arrow-control
                    value-format="HH:mm:ss"
                    v-model="form.timeAround"
                    range-separator="至"
                    start-placeholder="开始时间"
                    end-placeholder="结束时间"
                    placeholder="选择时间范围">
                  </el-time-picker>
                </el-form-item>
              </el-col>
            </el-row>

            <!--<div class="el-dialog__footer" style="border-top: none" v-if="isEditRuleFlag">
              <el-button type="primary" @click="onSaveRuleDetail" v-if="isEdit" >提交</el-button>

              <el-button type="primary" @click="onEditRuleDetail" v-else>编辑</el-button>
              &lt;!&ndash;<el-button @click="onReset" v-if="isEdit">重置</el-button>&ndash;&gt;
            </div>-->
          </el-form>

          <!--分组-->
          <div class="filter-group__wrapper">
            <el-button type="primary" @click="onAddGroup">
              <i class="el-icon-circle-plus"></i>
              新增分组
            </el-button>
            <alarm-rule-group
              v-for="(group, groupIndex) in groupArr"
              :rule_id="editDetailId"
              ref="alarmRuleGroup"
              :group="group"
              :dictCategoryAll="dictCategoryAll"
              :isNewAddFlag.sync="group.isNewAddFlag"
              :isEditRuleFlag="isEditRuleFlag"
              :key="group.id"
              :groupIndex="groupIndex"
              :uebaMetaDataList="uebaMetaDataList"
              @onDelGroupCb="onDelGroup(groupIndex, group)"
              @submitFormCn="submitFormCn"></alarm-rule-group>
          </div>

          <div class="el-dialog__footer" v-if="!isEditRuleFlag">
            <el-button type="primary" @click="onSave">提交</el-button>

            <!--<el-button type="primary" @click="onEdit">编辑</el-button>
            <el-button @click="onReset" v-if="isEdit">重置</el-button>-->
          </div>
        </div>
      </el-aside>
    </el-container>
  </div>
</template>

<script>
import mixins from '@/mixins/index.js'

import alarmRuleGroup from './components/alarm-rule-group'

import { isRegExpContent } from '@/utils/index'

export default {
  components: {
    alarmRuleGroup
  },
  mixins: [mixins],
  filters: {
    statusFilter(val) {
      const statusMap = {
        0: '禁用',
        1: '启用',
      }
      return statusMap[val]
    },
  },

  data() {
    return {
      that: this,
      form: {
        name: '',
        description: '',
        category_id: '',
        interval_time: '',
        time_unit: 'hour', // 时间单位: hour或minute
        latest_hours: '', // 查询时间范围
        begin_time: '', // 开始时段:00:00:00
        end_time: '', // 结束时段:00:00:00
        timeAroundFlag: false,
        // 时间插件默认显示时分秒(这里的年月日只是保持格式一致使用,请忽略)
        // 默认:开始时段:00:00:00 结束时段:00:00:00
        timeAround: [new Date(2020, 6, 1, 0, 0, 0), new Date(2020, 6, 1, 0, 0, 0)],
        condition_groups: []
      },

      rules: {
        name: [
          { required: true, message: '请输入规则名称', trigger: 'blur' }
        ],
        category_id: [
          { required: true, message: '请选择规则分类', trigger: 'change' }
        ],
        description: [
          { required: true, message: '请输入规则描述', trigger: 'blur' }
        ],
        interval_time: [
          { required: true, message: '请输入执行频率', trigger: 'change' }
        ],
        latest_hours: [
          { required: true, message: '请输入时间范围', trigger: 'change' }
        ],
      },

      statusArr: [
        {
          value: 0,
          label: '禁用'
        },
        {
          value: 1,
          label: '启用'
        }
      ],
      timeUnitArr: [
        {
          conf_value: 'hour',
          conf_key: '分钟',
        },
        {
          conf_value: 'minute',
          conf_key: '小时',
        }
      ],

      // 规则类型(搜索)
      ruleTypesOptions: [],
      // 字段名称下拉选项
      uebaMetaDataList: [],

      searchForm: {
        name: '',
        status: null, // 0/1
        category_id: null,
      },
      listLoading: false,
      ruleDetailLoading: false,

      currentPage: 1,
      pageSize: 20,
      total: 0,
      tableData: [],

      // 编辑区域laodings
      editPartLoading: false,
      isEdit: true,
      isEditRuleFlag: false,

      // ----------

      /// 表单相关参数
      dataDict:{},
      // 预警类型
      alarmTypes: [],
      // 防火墙名称
      sysNameArr: [],
      // 规则类型
      ruleTypes: [],
      // 因为接口共用,前端必须控制传参字段正确
      paramsArr: [],

      // 区分编辑还是新增
      editDetailId: '',

      dictCategoryAll: [],

      groupArr: [{
        groupId: Date.now(),
        isNewAddFlag: true
      }]
    };
  },
  created() {
    this.getDictCategoryAll();
    this.getDataDictQuery();
    this.getUebaMetaDataList();
    this.initTableData();
  },
  mounted() {

  },
  methods: {
    // 初始化列表数据
    async initTableData(val) {
      this.currentPage = val
      this.listLoading = true
      const options = {
        page: this.currentPage,
        page_size: this.pageSize,
        ...this.searchForm
      }
      await this.$API.uebaRuleList(options).then(res => {
        this.listLoading = false
        if (res && res.code === 0) {
          this.tableData = res.data.rows;
          // this.setCurrent(this.tableData[0])
          this.total = res.data.total_rows; //总数
          this.currentPage = res.data.page; //当前页码
        } else {
          this.$message.error(res.msg)
        }
      }).catch(error => {
        this.listLoading = false
        console.log(error)
      })
    },

    checkStatus(val) {
      const statusType = {
        '0': {
          text: '启用',
          postData: 1
        },
        '1': {
          text: '禁用',
          postData: 0
        }
      }
      return statusType[val]
    },

    // 启用/禁用
    handleStatusChange(row) {
      const statusText = this.checkStatus(row.status).text
      const statusPostData = this.checkStatus(row.status).postData

      this.$confirm(`确定${statusText}该告警规则么?`, statusText, {
        closeOnClickModal: false
      }).then(() => {
        const options = {
          id: row.id,
          status: statusPostData
        }
        this.$API.alarmRulesStatus(options).then(response => {
          if (response && response.code === 0) {
            this.initTableData(1)
          } else {
            this.$message.error(response.msg)
          }
        }).catch(error => {
          this.listLoading = false
          console.log(error)
        })
      }).catch((msg) => {
        console.log(msg)
      })
    },

    handleDelete(row, index) {
      let rule_ids = [].concat(row.id)
      const options = {
        ids: rule_ids
      }

      this.$confirm('此操作将删除该告警规则,确认删除么?', {
        confirmButtonText: '确定',
        cancleButtonText: '取消',
        type: 'warning'
      }).then(async() => {
        this.$API.uebaRuleDelete(options).then(response => {
          if (response && response.code === 0) {
            this.tableData.splice(index, 1)
            this.$message.success('删除成功!')

            // 如果删除当前编辑信息,则刷新返回新增信息状态
            if(rule_ids.includes(this.editDetailId)){
              this.handleAdd()
            }
          } else {
            this.$message.error(response.msg)
          }
        }).catch(error => {
          console.log(error)
        })
      }).catch(error => {
        console.log(error)
      })
    },

    // 查询
    async handleQuery() {
      this.initTableData(1)
    },

    // 新增
    handleAdd() {
      this.editPartLoading = true
      this.isEditRuleFlag = false
      this.editDetailId = ''
      this.setCurrent()

      this.initEditForm()
      setTimeout(()=>{
        // 默认显示
        this.editPartLoading = false
        this.clearValidate()
      },300)
    },

    // 初始编辑内容
    initEditForm() {
      const obj = {
        name: '',
        description: '',
        category_id: "",
        interval_time: '',
        time_unit: 'hour', // 时间单位: hour或minute
        latest_hours: '', // 查询时间范围
        begin_time: '', // 开始时段:00:00:00
        end_time: '', // 结束时段:00:00:00
        timeAroundFlag: false,
        // 时间插件默认显示时分秒(这里的年月日只是保持格式一致使用,请忽略)
        // 默认:开始时段:00:00:00 结束时段:00:00:00
        timeAround: [new Date(2020, 6, 1, 0, 0, 0), new Date(2020, 6, 1, 0, 0, 0)],
        condition_groups: []
      }
      this.groupArr = [{
        groupId: Date.now(),
        isNewAddFlag: true
      }]
      Object.assign(this.form, obj)
    },

    handleSizeChange(v) {
      this.pageSize = v;
      this.initTableData(1);
    },

    handleCurrentChange(v) {
      this.initTableData(v);
    },

    setCurrent(row) {
      this.$refs.singleTable.setCurrentRow(row);
    },

    // 数据字典
    getDataDictQuery() {
      const options = {
        code_list: ['ueba_rule_type']
      }
      this.$API.dataDictQuery(options).then(response => {
        if (response && response.code === 0) {
          this.ruleTypesOptions = response.data.ueba_rule_type
        } else {
          this.$message.error(response.msg)
        }
      }).catch(error => {
        console.log(error)
      })
    },

    // 编辑详情
    handleCurrentCulumn(row) {
      // 编辑提交会触发此事件
      if(!row){
        return
      }
      this.editPartLoading = true
      this.isEdit = false
      this.editDetailId = row.id
      this.groupArr = []
      const options = {
        id: row.id
      }
      this.clearValidate()
      setTimeout(()=>{
        this.$API.uebaRuleDetail(options).then(response => {
          if (response && response.code === 0) {
            const {
              begin_time, category_id, description, end_time, id, interval_time, latest_hours,
              name, time_unit, updated_time, updated_by
            } = response.data

            let timeAroundFlag = false
            let timeAround = false

            if(begin_time !== '00:00:00' || end_time !== '00:00:00'){
              timeAroundFlag = true
              timeAround = [new Date(`2020-6-1 ${begin_time}`), new Date(`2020-6-1 ${end_time}`)]
            }else {
              timeAround = [new Date(2020, 6, 1, 0, 0, 0), new Date(2020, 6, 1, 0, 0, 0)]
            }

            let obj = { begin_time, category_id, description, end_time, id, updated_by, updated_time,
              interval_time, latest_hours, name, time_unit, timeAroundFlag, timeAround,
            }

            Object.assign(this.form, obj)
            this.editPartLoading = false
            this.isEditRuleFlag = true
          } else {
            this.$message.error(response.msg)
          }
        }).catch(error => {
          console.log(error)
        })


        this.$API.uebaRuleConditionGroupList({rule_id: options.id}).then(response => {
          if (response && response.code === 0) {
            const data = response.data

            Object.assign(this.form, {
              condition_fields: data,
            })

            this.groupArr = this.groupArr.concat(data)
          } else {
            this.$message.error(response.msg)
          }
        }).catch(error => {
          console.log(error)
        })
      },300)

    },

    submitFormCn(form) {
      // 过滤,去除重复的groupId
      const index = this.form.condition_groups.findIndex(item => item.groupId === form.groupId)
      if(index !== -1){
        this.form.condition_groups.splice(index, 1)
      }

      this.form.condition_groups.push(form)
    },

    onSave(){
      // 获取子组件,通过refs访问子组件方法,校验条件值
      const alarmRuleGroup = this.$refs['alarmRuleGroup']
      alarmRuleGroup.forEach( subVue => {
        subVue.submitForm()
      })

      this.$refs['ruleForm'].validate(valid => {
        // 创建分组数量跟内容校验通过数是否一致
        const { condition_groups } = this.form
        if(alarmRuleGroup.length !== condition_groups.length){
          return
        }

        if (valid) {
          let {
            name, description, category_id, interval_time, time_unit, latest_hours,
            begin_time, end_time, timeAroundFlag, timeAround, condition_groups,
          } = this.form

          // 时间传参处理
          if(timeAroundFlag) {
            begin_time = timeAround[0]
            end_time = timeAround[1]
          }else {
            begin_time = '00:00:00'
            end_time = '00:00:00'
          }

          let options = {
            name, description, category_id, interval_time, time_unit, latest_hours, begin_time, end_time, condition_groups,
          }

          if(!this.editDetailId){
            this.$API['uebaRuleCreate'](options).then(response => {
              if (response && response.code === 0) {
                this.$message.success(response.msg)
                this.initTableData(1)

                // 编辑成功,切换回新增状态
                if(this.editDetailId){
                  this.editDetailId = ''
                }

                this.initEditForm()

              } else {
                this.$message.error(response.msg)
              }
            }).catch(error => {
              console.log(error)
            })
          }else {
            options.rule_id = this.editDetailId

            // 规则条件组编辑

          }
        } else {
          console.log('error submit!!')
        }
      })
    },

    onReset(){

    },

    clearValidate() {
      this.$refs.ruleForm.clearValidate();
    },

    onAddGroup() {
      this.groupArr.push({
        groupId: Date.now(),
        isNewAddFlag: true
      })
    },

    handleDeleteGroup(id, groupIndex) {
      let rule_ids = [id]
      const options = {
        ids: rule_ids
      }

      this.$confirm('此操作将删除该规则条件组,确认删除么?', {
        confirmButtonText: '确定',
        cancleButtonText: '取消',
        type: 'warning'
      }).then(async() => {
        this.$API.uebaRuleConditionGroupDelete(options).then(response => {
          if (response && response.code === 0) {
            this.$message.success('删除成功!')
            this.groupArr.splice(groupIndex, 1)
          } else {
            this.$message.error(response.msg)
          }
        }).catch(error => {
          console.log(error)
        })
      }).catch(error => {
        console.log(error)
      })
    },

    onDelGroup(groupIndex, group) {
      if(this.groupArr.length <= 1){
        this.$message.warning('最少有一个条件组')
        return
      }
      if(this.isEditRuleFlag){
        this.handleDeleteGroup(group.id, groupIndex)
        return
      }

      this.groupArr.splice(groupIndex, 1)
    },

    // 规则名称下拉
    getUebaMetaDataList() {
      this.$API.uebaMetaDataList().then(response => {
        if (response && response.code === 0) {
          this.uebaMetaDataList = response.data
        } else {
          this.$message.error(response.msg)
        }
      }).catch(error => {
        console.log(error)
      })
    },

    onEditRuleDetail() {
      this.isEdit = true
    },

    onSaveRuleDetail() {
      this.ruleDetailLoading = true
      setTimeout(()=>{
        this.$refs['ruleForm'].validate(valid => {
          if (valid) {
            let {
              name, description, category_id, interval_time, time_unit, latest_hours,
              begin_time, end_time, timeAroundFlag, timeAround, id
            } = this.form

            // 时间传参处理
            if(timeAroundFlag) {
              // 默认值
              if(typeof timeAround[0] === 'object' || typeof timeAround[1] === 'object'){
                begin_time = end_time
                end_time = end_time
              }else {
                begin_time = timeAround[0]
                end_time = timeAround[1]
              }
            }else {
              begin_time = '00:00:00'
              end_time = '00:00:00'
            }

            let options = {
              name, description, category_id, interval_time, time_unit, latest_hours, begin_time, end_time, id
            }

            this.$API['uebaRuleModify'](options).then(response => {
              if (response && response.code === 0) {
                this.$message.success('修改成功')
                this.ruleDetailLoading = false
                this.isEdit = false
              } else {
                this.$message.error(response.msg)
              }
            }).catch(error => {
              console.log(error)
            })
          } else {
            console.log('error submit!!')
          }
        })
      },300)
    },

    getDictCategoryAll() {
      this.$API
        .dataDictCategoryAll({
          name: 'UEBA数据字典' // TODO 这里需要约定下,不能随意改
        })
        .then(async (res) => {
          if (res.code === 0) {
            const curdDictCategoryId = res.data[0].id
            let { data } = await this.$API.dataDictList({
              category_id: curdDictCategoryId,
              page: 1,
              page_size: 200000
            });
            this.dictCategoryAll = data.rows.map(item=>({
              conf_value: item.name,
              conf_key: item.code
            }))
          }
        });
    },
  }
};
</script>

<style lang="scss" scoped>
.el-container {
  width: 100%;
  min-height: calc(100vh - 200px);
  margin-bottom: 30px;
  .el-aside {
    margin-left: 10px;
    border: 1px solid #eee;
    /deep/ .el-input-group__append {
      background: #9571e9;
      color: #fff;
      border: none;
    }
    .levelUl {
      .levelLi {
        padding: 0 20px;
        line-height: 40px;
        border-bottom: 1px solid #eee;
        display: flex;
        justify-content: space-between;
        flex-direction: row;
        align-items: center;
        position: relative;
      }
      .levelLi:hover {
        background: #9571e9;
        color: #fff;
      }
    }
    .tree-inner {
      height: calc(100% - 100px);
      overflow-y: scroll;
      .el-tree {
        font-size: 12px;
      }
    }
  }
  .el-main {
    padding: 0;
    .table-inner {
      height: calc(100% - 55px);
      overflow-y: scroll;
    }
  }

  /deep/ .el-range-editor.is-disabled input {
    color: #000!important;
  }
}
.person-pagination {
  width: 100%;
  //  flex: flex;
  position: relative;
  left: 0;
}


.el-aside-form{
  margin: 10px 10px 10px 0px;
}

.status-circle{
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
}

.status-success {
  color: #00b94d;
}
.status-danger {
  color: #f55;
}

.filter-group__wrapper {
  padding: 20px;
  border-top: 1px solid #c1bfc7;
}
</style>

  • 子组件:alarmRuleGroup
<template>
  <!--自定义字段-->
  <div class="filter-group">
    <el-card class="filter-group__item" v-loading="loading">
      <div class="title el-button--text">
        <span>条件分组 {{groupIndex + 1}}</span>
        <div class="title-rgith">
          <el-button icon="el-icon-delete" @click="onDelGroup" :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit">删除分组</el-button>

          <div v-if="isEditRuleFlag">
            <el-button type="primary" @click="onSave" v-if="!isNewAddFlag && isEdit" >提交</el-button>

            <el-button type="primary" @click="onEdit" v-if="!isNewAddFlag && !isEdit">编辑</el-button>
            <!--<el-button @click="onReset" v-if="isEdit">重置</el-button>-->
            <el-button type="primary" v-if="isNewAddFlag" @click="onAddNew">新增</el-button>
          </div>
        </div>
      </div>

      <p class="sub-title el-button--text"><span>基础条件:</span></p>
      <el-form ref="ruleForm" :model="form" label-width="70px">

        <el-row :gutter="24">
          <el-col :span="7">
            <el-form-item
              label="操作最小次数" prop="min_counts" label-width="88px"
              :rules="[
                { required: true, message: '请输入操作最小次数', trigger: 'blur' }
              ]">
              <el-input-number
                v-model="form.min_counts"
                :min="1"
                :max="1000000"
                label="描述文字"
                :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit"
                style="width: 120px">
              </el-input-number>
            </el-form-item>
          </el-col>
        </el-row>

        <p class="sub-title el-button--text rule"><span>条件规则:</span></p>
        <el-row :gutter="24" v-for="(item, index) in form.condition_fields">
          <el-col :span="7" class="col-item">
            <el-form-item
              label="字段名称"
              :key="item.key"
              :prop="'condition_fields.' + index + '.field_id'"
              :rules="[
                { required: true, message: '请选择字段名称', trigger: 'change' }
              ]">
              <el-select
                v-model="item.field_id"
                style="width: 100%;"
                placeholder="请选择"
                :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit"
                @change="changeRuleName(item)"
              >
                <el-option
                  v-for="item in uebaMetaDataList"
                  :key="item.id"
                  :label="item.field_name"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="7">
            <el-form-item
              label="条件关系"
              :prop="'condition_fields.' + index + '.operation'"
              :key="item.key"
              label-width="70px"
              :rules="[
                { required: true, message: '请选择条件关系', trigger: 'change' }
              ]">
              <el-select
                v-model="item.operation"
                style="width: 100%;"
                clearable
                placeholder="请选择"
                :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit"
              >
                <el-option
                  v-for="operation in operationArr[item.field_type]"
                  :key="operation.conf_value"
                  :label="operation.conf_key"
                  :value="operation.conf_value"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="7">
            <!--
              field_type: string、enum、date
            -->
            <el-form-item
              label="条件值"
              :prop="'condition_fields.' + index + '.input_content'"
              :key="item.key"
              label-width="60px"
              :rules="[
                { required: true, message: '条件值不能为空', trigger: 'change' }
              ]">
              <template>
                <div class="inner-datc-dict" v-show="item.field_type === 'string'">
                  <span class="el-form-item__label">引用数据字典:</span>
                  <el-checkbox v-model="item.userDictFlag" :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit" @change="onSwitchUseDict(item)"></el-checkbox>
                </div>
              </template>

              <el-input
                v-show="item.field_type === 'string' && item.values_type === 'text'"
                v-model.trim="item.input_content"
                clearable
                :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit"
                placeholder="请输入条件值"
              />

              <el-date-picker
                v-show="item.field_type === 'date' || item.field_type ===  'datetime'"
                v-model="item.input_content"
                type="date"
                value-format="yyyy-MM-dd"
                :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit"
                style="display: inline-block"
                placeholder="选择日期">
              </el-date-picker>

              <el-select
                v-show="item.field_type === 'enum' || item.values_type === 'dict_code'"
                v-model="item.input_content"
                style="width: 100%;"
                clearable
                :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit"
                placeholder="请选择"
              >
                <el-option
                  v-for="i in dictCodeArr"
                  :key="i.conf_value"
                  :label="i.conf_value"
                  :value="i.conf_key"
                />
              </el-select>
            </el-form-item>

          </el-col>
          <el-col :span="3">
            <el-button type="danger" icon="el-icon-minus" :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit" circle @click="onDel(index)"></el-button>
            <el-button v-show="(index+1) === form.condition_fields.length" type="primary" icon="el-icon-plus" :disabled="!isNewAddFlag && isEditRuleFlag && !isEdit" circle @click="onAdd"></el-button>
          </el-col>
        </el-row>

      </el-form>
      <!--<div class="el-dialog__footer" v-if="isEditRuleFlag">
        <el-button type="primary" @click="onSave" v-if="!isNewAddFlag && isEdit" >提交</el-button>

        <el-button type="primary" @click="onEdit" v-if="!isNewAddFlag && !isEdit">编辑</el-button>
        &lt;!&ndash;<el-button @click="onReset" v-if="isEdit">重置</el-button>&ndash;&gt;
        <el-button type="primary" v-if="isNewAddFlag" @click="onAddNew">新增</el-button>
      </div>-->
    </el-card>
    <el-divider>与</el-divider>
  </div>
</template>

<script>
  export default {
    name: 'alarm-rule-group',
    props: {
      group: {
        type: Object,
        default:() => {}
      },
      uebaMetaDataList: {
        type: Array,
        default:() => []
      },
      rule_id: {
        type: [Number,String],
        default: ''
      },
      dictCategoryAll: {
        type: Array,
        default: ()=>[]
      },
      // 区分父组件是否 【编辑状态】 还是 【新增状态】
      isEditRuleFlag: {
        type: Boolean,
        default: false
      },
      isNewAddFlag: {
        type: Boolean,
        default: false
      },
      groupIndex: {
        type: Number,
        default: 0
      },
    },
    created() {
      // 处理默认值
      let {
        condition_fields,
        id,
        min_counts
      } = this.group

      // 编辑
      if(id){
        this.form.id = id
        this.form.groupId = id
        this.form.min_counts = min_counts
        this.form.condition_fields = []
        condition_fields.forEach(item=> {
          this.changeRuleName(item, false)
          this.form.condition_fields.push({
            field_id: item.field_id,
            operation: item.operation,
            values_type: item.values_type, // text或dict_code(引用外部数据字典)
            userDictFlag: item.values_type === 'dict_code' ? true: false,
            input_content: item.input_values[0], // TODO 需要把input_content的内容放在input_values传给后端
            input_values: item.input_values, // 统一数组形式传给后端
            field_type: item.field_type,
            key: Date.now()
          })
        })
      }
    },
    data() {
      return {
        loading: false,
        isEdit: false,
        dictCodeArr: [],
        form: {
          groupId: Date.now(), // 前端标记识别用
          min_counts: '',
          condition_fields: [
            {
              field_id: '',
              operation: '',
              values_type: 'text', // text或dict_code(引用外部数据字典)
              userDictFlag: false,
              input_content: '', // 需要把input_content的内容放在input_values传给后端
              input_values: [], // 统一数组形式传给后端
              field_type: 'string'
            },
          ],
        },
        operationArr: {
          string: [
            { conf_value: 'equal', conf_key: '等于' },
            { conf_value: 'not_equal ', conf_key: '不等于' },
            { conf_value: 'lt', conf_key: '小于' },
            { conf_value: 'lte', conf_key: '小于等于' },
            { conf_value: 'gt', conf_key: '大于' },
            { conf_value: 'gte', conf_key: '大于等于' },
            { conf_value: 'between', conf_key: '范围于' },
          ],
          enum: [
            { conf_value: 'equal', conf_key: '等于' },
            { conf_value: 'not_equal ', conf_key: '不等于' },
            { conf_value: 'in', conf_key: '包含于' },
            { conf_value: 'not_in', conf_key: '不包含于' },
            { conf_value: 'like', conf_key: '匹配' },
            { conf_value: 'not_like', conf_key: '不匹配' },
          ],
          date: [
            { conf_value: 'equal', conf_key: '等于' },
            { conf_value: 'not_equal ', conf_key: '不等于' },
            { conf_value: 'in', conf_key: '包含于' },
            { conf_value: 'not_in', conf_key: '不包含于' },
            { conf_value: 'like', conf_key: '匹配' },
            { conf_value: 'not_like', conf_key: '不匹配' },
          ],
          datetime: [
            { conf_value: 'equal', conf_key: '等于' },
            { conf_value: 'not_equal ', conf_key: '不等于' },
            { conf_value: 'in', conf_key: '包含于' },
            { conf_value: 'not_in', conf_key: '不包含于' },
            { conf_value: 'like', conf_key: '匹配' },
            { conf_value: 'not_like', conf_key: '不匹配' },
          ],
        },
      }
    },
    methods: {
      // 数据字典
      getDataDictQuery(code_list) {
        const options = {
          code_list: [code_list],
        }
        this.$API.dataDictQuery(options).then(response => {
          if (response && response.code === 0) {
            this.dictCodeArr = response.data[code_list]
          } else {
            this.$message.error(response.msg)
          }
        }).catch(error => {
          console.log(error)
        })
      },
      // 切换规则名称
      changeRuleName(item, refresh = true) {
        const curMeta = this.uebaMetaDataList.find(meta => meta.id == item.field_id)
        item.field_type = curMeta['field_type']
        if(curMeta.dict_code) {
          this.getDataDictQuery(curMeta.dict_code)
        }
        if(item.values_type === 'dict_code'){
          this.dictCodeArr = this.dictCategoryAll
        }
        if(refresh){
          item.operation = ''
          item.input_content = ''
        }
      },
      // 引用数据字典
      onSwitchUseDict(item) {
        item.values_type = item.userDictFlag ? 'dict_code' : 'text'
        this.dictCodeArr = this.dictCategoryAll
      },
      onDel(index) {
        if(this.form.condition_fields.length <= 1){
          this.$message.warning('最少有一个条件')
          return
        }
        this.form.condition_fields.splice(index, 1)
      },
      onAdd() {
        this.form.condition_fields.push({
          field_id: '',
          operation: '',
          values_type: 'text', // text或dict_code(引用外部数据字典)
          userDictFlag: false,
          input_content: '', // 需要把input_content的内容放在input_values传给后端
          input_values: [], // 统一数组形式传给后端
          field_type: 'string',
          key: Date.now()
        })
      },
      onDelGroup() {
        this.$emit('onDelGroupCb')
      },
      // 提交校验数据
      submitForm(fn) {
        console.log('进来了')
        this.$refs['ruleForm'].validate((valid) => {
          if (valid) {
            if(fn){
              fn()
              return
            }

            this.form.condition_fields.forEach(item => {
              item.input_values = [item.input_content]
            })
            this.$emit('submitFormCn', this.form)
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      },
      // 重置
      resetForm() {
        this.$refs['ruleForm'].resetFields();
      },

      // 规则条件组编辑
      onEditRuleGroup() {
        this.loading = true
        this.isEdit = false
        setTimeout(()=>{
          this.form.condition_fields.forEach(item => {
            item.input_values = [item.input_content]
          })

          this.$API.uebaRuleConditionGroupModify(this.form).then(response => {
            if (response && response.code === 0) {
              this.loading = false
              this.$message.success('修改成功')
            } else {
              this.$message.error(response.msg)
            }
          }).catch(error => {
            console.log(error)
          })
        },300)
      },

      onSave() {
        this.submitForm(this.onEditRuleGroup)
      },
      onEdit() {
        this.isEdit = true
      },
      onReset() {

      },
      // 详情状态 新增规则组
      onAddNew() {
        this.$refs['ruleForm'].validate((valid) => {
          if (valid) {
            this.loading = true
            this.isEdit = false
            setTimeout(()=>{
              this.form.condition_fields.forEach(item => {
                item.input_values = [item.input_content]
              })

              const options = {
                condition_groups: [this.form],
                rule_id: this.rule_id
              }

              this.$API.uebaRuleConditionGroupAdd(options).then(response => {
                if (response && response.code === 0) {
                  this.loading = false
                  // 新增成功切换编辑状态,后端返回id
                  this.form.id = response.data[0]
                  this.$emit("update:isNewAddFlag", false)
                  this.$message.success('新增成功')
                } else {
                  this.$message.error(response.msg)
                }
              }).catch(error => {
                console.log(error)
              })
            },300)
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      },
    }
  }
</script>

<style lang="scss" scoped>
  .filter-group{
    .filter-group__item {
      margin-top: 16px;
      .title {
        font-size: 15px;
        font-weight: 600;
        padding-bottom: 14px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        cursor: pointer;
        .title-rgith {
          display: flex;
          align-items: center;
          .el-button{
            margin-right: 15px;
            &:last-child {
              margin-right: 0;
            }
          }
        }
      }
      .sub-title {
        margin-bottom: 20px;
        text-align: center;
        position: relative;
        font-size: 13px;
        font-weight: 600;
        &.rule {
          margin-bottom: 30px;
        }
        span {
          display: inline-block;
          background: #fff;
          position: relative;
          z-index: 1;
          width: 80px;
        }
        &:after {
          content: '';
          height: 0.2px;
          width: 100%;
          position: absolute;
          top: 50%;
          left: 0;
          transform: translateY(-50%);
          background: rgba(149,113,223,0.5);
        }
      }
    }

    .el-form-item {
      position: relative;
      .inner-datc-dict{
        position: absolute;
        top: -30px;
        left: 0;
      }
    }
  }

  .el-row{
    margin-top: 20px;
  }

  .el-date-editor.el-input {
    width: 100%;
  }

</style>

  • mixins方法
export default {
  filters: {
    // 下拉组件-根据code显示label
    selectOptionFilter(code, that, arr) {
      if (code !== '') {
        if (that.selectColumns(arr).length > 0) {
          let data = that.selectColumns(arr).find(v => v.code === code)
          if(data){
            return data.text
          }else {
            // 这里兼容【账号告警】下的 告警平台
            // 如果字典没有配置,则不做处理,直接返回
            return code
          }
        } else {
          return ''
        }
      }
      return ''
    }
  },
  methods: {
    selectColumns(arr) {
      let columnsInMixin = []
      if (arr && arr.length) {
        columnsInMixin = arr.map(v => {
          return {
            text: v.conf_value,
            code: v.conf_key
          }
        })
        return columnsInMixin
      } else {
        return []
      }
    }
  }
}

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

推荐阅读更多精彩内容