Prechádzať zdrojové kódy

1.0 添加 数据质量探查

allen 3 rokov pred
rodič
commit
f822f29d26

+ 630 - 0
src/views/modules/snoop/home.vue

@@ -0,0 +1,630 @@
+<!--  -->
+<template>
+  <div class=''>
+    <el-breadcrumb class="divi2" separator-class="el-icon-arrow-right">
+      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
+      <el-breadcrumb-item>数据质量探测</el-breadcrumb-item>
+    </el-breadcrumb>
+    <el-divider class="divi"></el-divider>
+
+    <el-form :inline="true" :model="dataForm" @keyup.enter.native="getMeasureOrJobList">
+      <el-form-item>
+        <el-button v-if="isAuth('snoop:save')" type="primary" @click="addHandle()">新建探测</el-button>
+        <el-button v-if="isAuth('snoop:delete')" type="danger" @click="measureAndJobDelete()"
+                   :disabled="dataListSelections.length <= 0">批量删除
+        </el-button>
+      </el-form-item>
+      <el-form-item>
+        <el-input v-model="dataForm.algorithmName" :placeholder="classificationtag=='度量'?'请输入度量名称':'请输入任务名称' " clearable
+                  @keyup.enter.native="pageIndex = 1;getMeasureList()"></el-input>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="pageIndex = 1;getMeasureList()">查询</el-button>
+      </el-form-item>
+      <el-form-item>
+        <el-tag>筛选:</el-tag>
+        <el-select v-model="classificationtag" placeholder="类别" size="mini" style="width: 130px;"
+                   @change="pageIndex = 1;getMeasureOrJobList()">
+          <el-option v-for="data in classification" :key="data" :label="data" :value="data">
+          </el-option>
+        </el-select>
+      </el-form-item>
+    </el-form>
+
+
+    <div v-if="classificationtag === '度量'" >
+      <el-table key="度量" :data="measureList" border
+                v-loading="measDataListLoading"
+                @selection-change="SelectionChange"
+                style="width: 100%;">
+        <el-table-column type="selection" header-align="center" align="center" width="50">
+        </el-table-column>
+        <el-table-column prop="Id" header-align="center" align="center" width="80" label="序号"
+                         type="index" :index='(index)=>{return (index+1) + (this.pageIndex-1)*this.pageSize}'>
+        </el-table-column>
+        <el-table-column prop="measureName" header-align="center" align="center" label="度量名称">
+        </el-table-column>
+        <el-table-column prop="measureType" header-align="center" align="center" label="度量类型"></el-table-column>
+        <!--        <el-table-column prop="username" header-align="center" align="center" label="创建人">-->
+        <!--        </el-table-column>-->
+        <!--        <el-table-column prop="createTime" header-align="center" align="center" label="创建时间">-->
+        <!--        </el-table-column>-->
+        <el-table-column prop="remark" header-align="center" align="center" label="备注">
+        </el-table-column>
+        <el-table-column fixed="right" header-align="center" align="center" width="250" label="操作">
+          <template slot-scope="scope">
+            <el-button type="text" size="small"  v-if="isAuth('snoop:measure:look')" @click="userwatch(scope.row)">查看</el-button>
+            <el-button type="text" size="small"  v-if="isAuth('snoop:delete')"  @click="measureDeleteHandle(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <div v-if="classificationtag === '任务'">
+      <el-table key="任务" :data="jobList" border
+                v-loading="jobDataListLoading"
+                @selection-change="SelectionChange"
+                style="width: 100%;">
+        <el-table-column type="selection" header-align="center" align="center" width="50">
+        </el-table-column>
+        <el-table-column prop="Id" header-align="center" align="center" width="80" label="序号"
+                         type="index" :index='(index)=>{return (index+1) + (this.pageIndex-1)*this.pageSize}'>
+        </el-table-column>
+        <el-table-column prop="jobName" header-align="center" align="center" label="任务名称">
+        </el-table-column>
+        <el-table-column prop="jobState.previousFireTime" header-align="center" align="center" label="上次执行时间">
+          <template slot-scope="scope">
+            {{ scope.row.jobState.previousFireTime | timeFormater }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="jobState.nextFireTime" header-align="center" align="center" label="下次执行时间">
+          <template slot-scope="scope">
+            {{ scope.row.jobState.nextFireTime | timeFormater }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="state" header-align="center" align="center" label="状态">
+          <template slot-scope="scope">
+            <el-tag v-if="scope.row.jobState.state === 'NORMAL'" size="small">正常</el-tag>
+            <el-tag v-else size="small" type="danger">暂停</el-tag>
+          </template>
+        </el-table-column>
+        <!--        <el-table-column prop="username" header-align="center" align="center" label="创建人">-->
+        <!--        </el-table-column>-->
+        <!--        <el-table-column prop="createTime" header-align="center" align="center" label="创建时间">-->
+        <!--        </el-table-column>-->
+
+        <el-table-column fixed="right" header-align="center" align="center" width="250" label="操作">
+          <template slot-scope="scope">
+            <el-button v-if="isAuth('snoop:job:look')" type="text" size="small" @click="jobWatch(scope.row)">查看
+            </el-button>
+            <el-button v-if="isAuth('snoop:job:delete')" type="text" size="small"
+                       @click="jobDeleteHandle(scope.row)">删除
+            </el-button>
+            <el-button v-if="isAuth('snoop:job:pause')" type="text" size="small"
+                       @click="jobPauseHandle(scope.row)" >
+              暂停
+            </el-button>
+            <el-button v-if="isAuth('snoop:job:resume')" type="text" size="small"
+                       @click="jobResumeHandle(scope.row)">恢复
+            </el-button>
+            <el-button v-if="isAuth('snoop:job:result')" type="text" size="small" @click="jobResHandle(scope.row)">结果
+            </el-button>
+            <el-button v-if="isAuth('sys:schedule:log')" type="text" size="small" @click="jobLogHandle(scope.row)">日志
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex"
+                   :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalPage"
+                   layout="total, sizes, prev, pager, next, jumper">
+    </el-pagination>
+    <snoop v-if="addVisible" ref="addS" @refreshDataList="getMeasureList"></snoop>
+    <measureUserWatch v-if="newWatchVisible" ref="measureWatch"></measureUserWatch>
+    <job-watch v-if="jobWatchVisible" ref="jobWatch"></job-watch>
+    <job-log v-if="jobLoghVisible" ref="jobLog"></job-log>
+    <jobResultVisiEchars v-if="jobResVisible" ref="jobRes" ></jobResultVisiEchars>
+  </div>
+</template>
+
+<script>
+// import AlgAdd from './alg-add.vue';
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+// import AddOrUpdate from "./dataset-add-or-update";
+// import Watch from "./dataset-watch";
+import snoop from './snoop-add'
+import measureUserWatch from './measureUserWatch'
+import jobWatch from './jobWatch'
+import jobLog from './jobLog'
+// import jobResultVisi from './jobResult_visi'
+import jobResultVisiEchars from './jobResult_visi_echars'
+export default {
+  // import引入的组件需要注入到对象中才能使用
+  data () {
+    return {
+      dataForm: {
+        algorithmName: ''
+      },
+      userID: this.$store.state.user.id, // 用户编号
+      // userName: this.$store.state.user.name, // 用户编号
+      userName: 'test',
+      measureList: [],    // 分页使用  当前显示的度量数据
+      measureListAll: [],  // 分页使用  所用的度量数据
+      jobList: [],   // 分页使用  当前显示的任务数据
+      jobListAll: [],  // 分页使用  所用的任务数据
+      pageIndex: 1,
+      pageSize: 10,
+      totalPage: 0,
+      jobDataListLoading: false,
+      measDataListLoading: false,
+      dataListSelections: [],
+      addOrUpdateVisible: false,
+      newWatchVisible: false,
+      jobWatchVisible: false,
+      addVisible: false,
+      jobLoghVisible: false,
+      jobResVisible: false,
+      classificationtag: '',
+      classification: ['度量', '任务']
+    }
+  },
+  components: {
+    // AlgAdd
+    // AddOrUpdate,
+    // Watch,
+    // Add
+    snoop,
+    measureUserWatch,
+    jobWatch,
+    jobLog,
+    jobResultVisiEchars
+  },
+
+  activated () {
+    // this.getMeasureList()
+    // this.getJobList()
+    this.classificationtag = this.classification[0]
+    var sign = this.$route.query.status
+    if (sign) {
+      this.classificationtag = this.classification[sign]
+      this.getMeasureOrJobList()
+    }
+  },
+  mounted () {
+    this.getMeasureList()
+  },
+  filters: {
+    timeFormater (value) {
+      var date = new Date(value)
+      let years = date.getFullYear()
+      let month = (date.getMonth() + 1) > 10 ? date.getMonth() + 1 : '0' + (date.getMonth() + 1)
+      let day = (date.getDate() >= 10) ? date.getDate() : '0' + date.getDate()
+      let hours = (date.getHours() >= 10) ? date.getHours() : '0' + date.getHours()
+      let minutes = (date.getMinutes() >= 10) ? date.getMinutes() : '0' + date.getMinutes()
+      let seconds = (date.getSeconds() >= 10) ? date.getSeconds() : '0' + date.getSeconds()
+      if (value) {
+        return years + '-' + month + '-' + day +
+          ' ' + hours + ':' + minutes + ':' + seconds
+      } else {
+        return 'N/A'
+      }
+    }
+  },
+  methods: {
+    getMeasureOrJobList () {
+      if (this.classificationtag === '度量') {
+        this.getMeasureList()
+      } else {
+        this.getJobList()
+      }
+    },
+    // 获取数据列表
+    getMeasureList () {
+      this.measureListAll = []
+      this.measureList = []
+      this.measDataListLoading = true
+
+      // this.measureList = [
+      //   {'measureID': 1, 'measureName': 'demo1', 'measureType': 'profiling', 'remark': '测试'},
+      //   {'measureID': 1, 'measureName': 'demo1', 'measureType': 'profiling', 'remark': '测试'}
+      //
+      // ]
+      this.$http({
+        url: this.$http.snoopUrl('/v1/measures/owner/') + this.userName,
+        methods: 'get'
+      }).then((datas) => {
+        if (datas && datas.status === 200) {
+          var res = datas.data.reverse()
+          console.log(res)
+          res.map((info) => {
+            var measure = {
+              'measureID': info['id'],
+              'measureName': info['name'],
+              'measureType': info['dq.type'],
+              'remark': info['description']
+            }
+            this.measureListAll.push(measure)
+          })
+        }
+        this.unique(this.measureListAll)
+        this.measureList = this.measureListAll.slice(0, this.pageSize)
+        this.totalPage = this.measureListAll.length
+        this.measDataListLoading = false
+      })
+    },
+    toCamel (myString) {
+      return myString.replace(/[.]([a-z])/g, function (g) {
+        return g[1].toUpperCase()
+      })
+    },
+
+    swapJson (json) {
+      const ret = {}
+      for (const key in json) {
+        ret[this.toCamel(key)] = json[key]
+      }
+      return ret
+    },
+    getJobList () {
+      this.jobListAll = []
+      this.jobDataListLoading = true
+      const self = this
+      this.$http({
+        url: this.$http.snoopUrl('/v1/jobs'),
+        methods: 'get'
+      }).then((datas) => {
+        if (datas && datas.status === 200) {
+          var data = datas.data
+          const trans = Object.keys(data).map(function (index) {
+            const job = self.swapJson(data[index])
+            job.jobState.previousFireTime = job.jobState.previousFireTime < 0 ? '' : job.jobState.previousFireTime
+            return job
+          })
+          this.jobListAll = Object.assign([], trans).reverse()
+          this.jobList = this.jobListAll.slice(0, this.pageSize)
+          this.totalPage = this.jobListAll.length
+        }
+
+        this.jobDataListLoading = false
+      })
+    },
+    // 去重
+    unique (arr) {
+      var ss = []
+      for (var i = 0; i < arr.length; i++) {
+        ss[i] = arr[i].measureName
+      }
+      return Array.from(new Set(ss))
+    },
+    // 每页数
+    sizeChangeHandle (val) {
+      this.pageSize = val
+      this.pageIndex = 1
+      if (this.classificationtag === '任务') {
+        let start = (this.pageIndex - 1) * this.pageSize
+        this.jobList = this.jobListAll.slice(start, this.pageSize)
+      } else {
+        let start = (this.pageIndex - 1) * this.pageSize
+        this.measureList = this.measureListAll.slice(start, this.pageSize)
+      }
+    },
+    // 当前页
+    currentChangeHandle (val) {
+      this.pageIndex = val
+      if (this.classificationtag === '任务') {
+        let start = (this.pageIndex - 1) * this.pageSize
+        let end = (this.pageIndex) * this.pageSize
+        this.jobList = this.jobListAll.slice(start, end)
+      } else {
+        let start = (this.pageIndex - 1) * this.pageSize
+        let end = (this.pageIndex) * this.pageSize
+        this.measureList = this.measureListAll.slice(start, end)
+      }
+    },
+    // **********************8多选
+    SelectionChange (val) {
+      this.dataListSelections = val
+    },
+
+    // 查看
+    userwatch (info) {
+      this.newWatchVisible = true
+      this.$nextTick(() => {
+        this.$refs.measureWatch.init(info)
+      })
+    },
+    jobWatch (info) {
+      this.jobWatchVisible = true
+      this.$nextTick(() => {
+        this.$refs.jobWatch.init(info)
+      })
+    },
+    jobLogHandle (info) {
+      this.jobLoghVisible = true
+      this.$nextTick(() => {
+        this.$refs.jobLog.init(info)
+      })
+    },
+    jobResHandle (info) {
+      this.jobResVisible = true
+      this.$nextTick(() => {
+        this.$refs.jobRes.init(info)
+      })
+    },
+
+    // 这里处理新建
+    addHandle () {
+      this.addVisible = true
+      this.$nextTick(() => {
+        this.$refs.addS.init()
+      })
+    },
+    jobResumeHandle (info) {
+      this.$confirm(
+        `确定对[任务名=${info.jobName}]进行[恢复]操作?`,
+        '提示',
+        {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }
+      ).then((data) => {
+        this.jobDataListLoading = true
+        //  进行后台请求
+        this.$http({
+          url: this.$http.snoopUrl('/v1/jobs/') + info.id + '?action=start',
+          method: 'put'
+        }).then((data) => {
+          if (data && data.status === 200) {
+            this.$message({
+              message: '操作成功',
+              type: 'success',
+              duration: 1500,
+              onClose: () => {
+                this.getJobList()
+              }
+            })
+          } else {
+            this.$message.error(info.jobName + '恢复失败')
+            this.getJobList()
+          }
+        })
+      })
+        .catch(() => {
+        })
+    },
+    jobPauseHandle (info) {
+      this.$confirm(
+        `确定对[任务名=${info.jobName}]进行[暂停]操作?`,
+        '提示',
+        {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }
+      ).then((data) => {
+        this.jobDataListLoading = true
+        //  进行后台请求
+        this.$http({
+          url: this.$http.snoopUrl('/v1/jobs/') + info.id + '?action=stop',
+          method: 'put'
+        }).then((data) => {
+          if (data && data.status === 200) {
+            this.$message({
+              message: '操作成功',
+              type: 'success',
+              duration: 1500,
+              onClose: () => {
+                this.getJobList()
+              }
+            })
+          } else {
+            this.$message.error(info.jobName + '恢复失败')
+            this.getJobList()
+          }
+        })
+      })
+        .catch(() => {
+        })
+    },
+    jobDeleteHandle (info) {
+      this.$confirm(
+        `确定对[任务名=${info.jobName}]进行 [删除] 操作?`,
+        '提示',
+        {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }
+      ).then(() => {
+        this.jobDataListLoading = true
+        //  进行后台请求
+        this.$http({
+          url: this.$http.snoopUrl('/v1/jobs/') + info.id,
+          method: 'delete'
+        }).then((data) => {
+          // console.log(data)
+          if (!data.data) {
+            this.$message({
+              message: '操作成功',
+              type: 'success',
+              duration: 1500,
+              onClose: () => {
+                this.getJobList()
+              }
+            })
+          } else {
+            this.$message.error(info.jobName + '删除失败')
+            this.getJobList()
+          }
+        })
+      })
+        .catch(() => {
+        })
+    },
+    // ********** 删除
+    measureDeleteHandle (info) {
+      this.$confirm(
+        `确定对[${info.measureName}]进行[删除]操作? 删除后,对应job也会被删除`,
+        '提示',
+        {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }
+      )
+        .then(() => {
+          this.$http({
+            url: this.$http.snoopUrl('/v1/measures/') + info.measureID,
+            method: 'delete'
+          }).then(({data}) => {
+            this.measDataListLoading = true
+            if (!data.data) {
+              this.$message({
+                message: '操作成功',
+                type: 'success',
+                duration: 1500
+              })
+              this.measDataListLoading = false
+              this.getMeasureList()
+              this.pageIndex = 1
+            } else {
+              this.$message.error(data.msg)
+            }
+          })
+        })
+        .catch(() => {
+        })
+    },
+    async measureAndJobDelete () {
+      var waitSecond = 1
+      if (this.classificationtag === '度量') {
+        var allMeasureName = []
+        for (var index in this.dataListSelections) {
+          allMeasureName.push(this.dataListSelections[index].measureName)
+        }
+        await this.$confirm(
+          `确定对[${allMeasureName}]进行[删除]操作? 删除后,对应job也会被删除`,
+          '提示',
+          {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }
+        ).then(() => {
+          this.measDataListLoading = true
+          let failure = []
+          for (var index = 0; index < this.dataListSelections.length; index++) {
+            waitSecond = waitSecond + 0.5
+            this.$http({
+              url: this.$http.snoopUrl('/v1/measures/') + this.dataListSelections[index].measureID,
+              method: 'delete'
+            }).then((data) => {
+              if (data.data) {
+                console.log(111)
+                failure.push(this.dataListSelections[index])
+              }
+            })
+          }
+
+          if (failure.length === 0) {
+            this.$message({
+              message: '操作成功',
+              type: 'success',
+              duration: 1500
+            })
+          } else {
+            var msg = ''
+            failure.forEach(x => {
+              msg = msg + ' ' + x.measureName
+            })
+            this.$message.error(msg)
+          }
+          setTimeout(() => {
+            this.measDataListLoading = false
+            this.getMeasureList()
+          }, waitSecond * 1000)
+        }).catch(() => {
+
+        })
+      } else {
+        var allJobName = []
+        for (var jobindex in this.dataListSelections) {
+          allJobName.push(this.dataListSelections[jobindex].jobName)
+        }
+        await this.$confirm(
+          `确定对[${allJobName}]进行[删除]操作? `,
+          '提示',
+          {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }
+        ).then(() => {
+          this.jobDataListLoading = true
+          let failure = []
+          for (var index = 0; index < this.dataListSelections.length; index++) {
+            waitSecond = waitSecond + 0.5
+            this.$http({
+              url: this.$http.snoopUrl('/v1/jobs/') + this.dataListSelections[index].id,
+              method: 'delete'
+            }).then((data) => {
+              if (data.data) {
+                console.log(111)
+                failure.push(this.dataListSelections[index])
+              }
+            })
+          }
+
+          if (failure.length === 0) {
+            this.$message({
+              message: '操作成功',
+              type: 'success',
+              duration: 1500
+            })
+          } else {
+            var msg = ''
+            failure.forEach(x => {
+              msg = msg + ' ' + x.jobName
+            })
+            this.$message.error(msg)
+          }
+          setTimeout(() => {
+            this.jobDataListLoading = false
+            this.getJobList()
+          }, waitSecond * 1000)
+        }).catch(() => {
+
+        })
+      }
+    }
+  }
+}
+</script>
+<style scoped>
+.divi {
+  display: block;
+  height: 1px;
+  width: 100%;
+  margin: 24px 0;
+  background-color: #dcdfe6;
+  position: relative;
+}
+
+.divi2 {
+  display: block;
+  height: 1px;
+  width: 100%;
+  position: relative;
+}
+
+.sele {
+  border: 1px solid #409eff;
+  border-radius: 5px;
+  box-sizing: border-box;
+  padding: 5px 0px;
+  margin: 10px;
+}
+</style>

+ 268 - 0
src/views/modules/snoop/job.vue

@@ -0,0 +1,268 @@
+<template>
+<div>
+  <el-breadcrumb class="divi2" separator-class="el-icon-arrow-right">
+    <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
+    <el-breadcrumb-item :to="{ path: '/snoop-home'}">数据质量探测</el-breadcrumb-item>
+    <el-breadcrumb-item>创建度量标准</el-breadcrumb-item>
+  </el-breadcrumb>
+  <el-divider class="divi"></el-divider>
+
+
+  <el-col :span="16" style="height: 100%;margin-left: 160px;">
+    <h4> 配置调度任务 :</h4>
+    <div style="height: 360px;overflow-scrolling: auto">
+      <el-form ref="jobForm" :model="jobcForm" label-width="120px" :rules="rules">
+        <el-form-item label="任务名称 :" prop="name">
+          <el-input v-model="jobcForm.name"></el-input>
+        </el-form-item>
+        <el-form-item label="度量名称 :" prop="measureID">
+          <el-select v-model="jobcForm.measureID"   @change="onChange" placeholder="请选择"              >
+            <el-option
+              v-for="measure in measures"
+              :key="measure.id"
+              :label="measure.name"
+              :value="measure.id"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="调度周期 :" prop="cron">
+          <el-input  v-model="jobcForm.cron" placeholder="请输入抽取周期"  clearable
+                     @focus="dialogVisible = true">
+          </el-input>
+          <el-dialog custom-class="cron-box-dialog-first" :visible.sync="dialogVisible" v-if="dialogVisible">
+            <mycron v-bind:extractionPeriod.sync="jobcForm.cron"></mycron>
+          </el-dialog>
+        </el-form-item>
+        <el-form-item label="请选择时间范围 :" prop="range">
+          <el-slider v-model="jobcForm.range" range show-stops :max="0" :min="-10" :marks="marks">
+          </el-slider>
+        </el-form-item>
+        <el-form-item style="float:right ;margin-top: 20px">
+          <el-button @click="visible = false;redirectHandle()">取消</el-button>
+          <el-button type="primary" @click="submitForm()">确定</el-button>
+        </el-form-item>
+      </el-form>
+
+    </div>
+  </el-col>
+</div>
+</template>
+
+<script>
+import mycron from '../visi/quartz/mycron'
+export default {
+  components: {mycron},
+  name: 'job',
+  data () {
+    // var checkExtractionPeriod = (rule, value, callback) => {
+    //   if (!value) {
+    //     return callback(new Error('抽取周期不能为空'))
+    //   } else {
+    //     return callback()
+    //   }
+    // }
+    return {
+      jobcForm: {
+        name: '',
+        measureID: '',
+        cron: '',
+        range: [-1, 0],
+        dropdownList: []
+      },
+      marks: {
+        '-1': '-1 day',
+        '-2': '-2 day',
+        '-3': '-3 day',
+        '-5': '-5 day',
+        '-7': '-7 day',
+        '-10': '-10 day'
+      },
+      dialogVisible: false,
+      measures: [],
+      rules: {
+        name: [
+          {required: true, message: '请输入任务名称', trigger: 'blur'},
+          {min: 3, max: 18, message: '长度在 3 到 18 个字符', trigger: 'blur'}
+        ],
+        measureID: [
+          { required: true, message: '请选择度量方案', trigger: 'change' }
+        ],
+        cron: [
+          // { validator: checkExtractionPeriod, trigger: 'blur' }
+          { required: true, message: '请输入调度周期', trigger: 'change' }
+        ]
+      }
+    }
+  },
+  methods: {
+    redirectHandle () {
+      this.$router.replace({ path: '/snoop-home' })
+    },
+
+    // 向后台提交数据
+    async finallySubmit () {
+      this.newJob = {
+        'job.name': this.jobcForm.name,
+        'job.type': 'batch',
+        'measure.id': this.jobcForm.measureID,
+        'cron.expression': this.jobcForm.cron,
+        // 'cron.time.zone': this.timezone,
+        'cron.time.zone': 'GMT+8:00',
+        // "predicate.config": {
+        // "interval": "1m",
+        // "repeat": 2
+        // },
+        'data.segments': [
+          // {
+          //   "data.connector.index": "source[0]",
+          //   "segment.range": {
+          //   "begin": "",
+          //   "length": ""
+          //   }
+          // },
+          // {
+          //   "data.connector.index": "target[0]",
+          //   "segment.range": {
+          //   "begin": "",
+          //   "length": ""
+          //   }
+          // }
+        ]
+      }
+      for (let i = 0; i < this.dropdownList.length; i++) {
+        var connector = this.dropdownList[i]
+        var begin = this.jobcForm.range[0]
+        var length = this.jobcForm.range[1] - this.jobcForm.range[0]
+        var beginStr = this.getTimeByUnit(begin, connector.size)
+        var lengthStr = this.getTimeByUnit(length, connector.size)
+        this.newJob['data.segments'].push({
+          'data.connector.name': connector.connectorname,
+          'as.baseline': true,
+          'segment.range': {
+            begin: beginStr,
+            length: lengthStr
+          }
+        })
+      }
+      if (this.dropdownList.length === 2) {
+        delete this.newJob['data.segments'][1]['as.baseline']
+      }
+      console.log(this.newJob)
+      await this.$http({
+        url: this.$http.snoopUrl('/v1/jobs'),
+        method: 'post',
+        data: this.$http.adornData(this.newJob)
+      }).then((datas) => {
+        // console.log(6666)
+        if (datas.status === 201) {
+          return true
+        } else {
+          return false
+        }
+      })
+        // .catch(err => {
+        //   console.log(err)
+        //   let response = JSON.parse(err.error)
+        //   console.log(response)
+        //   if (response.code === '40004') {
+        //     console.log('error', 'Error!', 'Job name already exists!')
+        //   } else {
+        //     console.log('error', 'Error!', response.message)
+        //   }
+        // })
+    },
+    submitForm () {
+      this.$refs['jobForm'].validate((valid) => {
+        if (valid) {
+          var submit = this.finallySubmit()
+
+          setTimeout(() => {
+            if (submit) {
+              this.$message({
+                message: '调度任务   ' + this.jobcForm.name + '   创建成功',
+                type: 'success'
+              })
+            } else {
+              this.$message({
+                message: '调度任务   ' + this.jobcForm.name + '   创建失败',
+                type: 'error'
+              })
+            }
+            this.$router.replace({path: '/snoop-home', query: {'status': '1'}})
+            this.$destroy()
+          }, 2 * 1000)
+        } else {
+          // console.log('error submit!!')
+          return false
+        }
+      })
+    },
+    getTimeByUnit (multiplier, unit) {
+      var regex = /^(\d+)([a-zA-Z]+)$/g
+      var arr = regex.exec(unit)
+      if (arr.length > 2) {
+        var n = parseInt(arr[1])
+        var unitStr = arr[2]
+        return ((n * multiplier).toString() + arr[2])
+      } else {
+        return multiplier.toString()
+      }
+    },
+    onChange (measure) {
+      this.dropdownList = []
+      console.log(measure)
+      for (let index in this.measures) {
+        var map = this.measures[index]
+        // console.log(measure)
+        // console.log(map.id)
+        if (measure === map.id) {
+          var source = map['data.sources']
+          for (let i = 0; i < source.length; i++) {
+            var connector = source[i].connector
+            if (connector['data.unit'] !== undefined) {
+              var table = connector.config.database + '.' + connector.config['table.name']
+              var size = connector['data.unit']
+              var connectorname = connector['name']
+              var detail = {
+                id: i + 1,
+                name: table,
+                size: size,
+                connectorname: connectorname
+              }
+              this.dropdownList.push(detail)
+            }
+          }
+        }
+      }
+    }
+  },
+  mounted () {
+    // console.log(this.$http.snoopUrl('/v1/measures/owner/') +  this.$store.state.user.name)  实际项目用这个
+    this.$http({
+      url: this.$http.snoopUrl('/v1/measures/owner/') + 'test',
+      method: 'get'
+    }).then((datas) => {
+      if (datas && datas.status === 200) {
+        var res = datas.data.reverse()
+        res.forEach((info) => {
+          if (info['process.type'] === 'BATCH') {
+            this.measures.push(info)
+          }
+        })
+      }
+    })
+    // for (var i = 0; i < 10; i++) {
+    //   obj = { 'id': i, 'name': 'demo' + i}
+    //   this.measures.push(obj)
+    // }
+  }
+}
+</script>
+
+<style scoped>
+.divi2 {
+  display: block;
+  height: 1px;
+  width: 100%;
+  position: relative;
+}
+</style>

+ 191 - 0
src/views/modules/snoop/jobLog.vue

@@ -0,0 +1,191 @@
+<template>
+<div>
+  <el-dialog
+    title="日志列表"
+    :close-on-click-modal="false"
+    v-if="visible"
+    :visible.sync="visible"
+    width="75%">
+    <el-table
+      :data="dataList"
+      border
+      v-loading="logDataListLoading"
+      height="460"
+      style="width: 100%;margin-top: -30px;">
+      <el-table-column
+        prop="id"
+        header-align="center"
+        align="center"
+        label="日志ID">
+      </el-table-column>
+      <el-table-column
+        prop="appId"
+        header-align="center"
+        align="center"
+        label="AppID">
+        <template slot-scope="scope">
+<!--          style="color:blue;cursor:pointer"-->
+          <div @click="detailData(scope.row.appUri)" >{{ scope.row.appId }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="timestamp"
+        header-align="center"
+        align="center"
+        label="执行时间"
+      >
+        <template slot-scope="scope">
+          {{scope.row.timestamp | timeFormater}}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="status"
+        header-align="center"
+        align="center"
+        label="状态">
+        <template slot-scope="scope">
+
+          <el-tag v-if="scope.row.state === 'STARTING'" size="small">排队中</el-tag>
+          <el-tag v-else-if="scope.row.state === 'FINDING'" size="small">启动中</el-tag>
+          <el-tag v-else-if="scope.row.state === 'RUNNING'" size="small">进行中</el-tag>
+          <el-tag v-else-if="scope.row.state === 'SUCCESS'" size="small">已完成</el-tag>
+          <el-tag v-else  size="small" type="danger" style="cursor: pointer;">失败</el-tag>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      @size-change="sizeChangeHandle"
+      @current-change="currentChangeHandle"
+      :current-page="pageIndex"
+      :page-sizes="[10, 20, 50, 100]"
+      :page-size="pageSize"
+      :total="totalPage"
+      layout="sizes, prev, pager, next">
+    </el-pagination>
+  </el-dialog>
+</div>
+</template>
+
+<script>
+export default {
+  name: 'jobLog',
+  data () {
+    return {
+      visible: true,
+      dataForm: {
+        id: ''
+      },
+      info: null,
+      dataList: [],
+      pageIndex: 1,
+      pageSize: 10,
+      totalPage: 0,
+      logDataListLoading: false
+    }
+  },
+  filters: {
+    timeFormater (value) {
+      // console.log('@', value)
+      var date = new Date(value)
+      let years = date.getFullYear()
+      let month = (date.getMonth() + 1) > 10 ? date.getMonth() + 1 : '0' + (date.getMonth() + 1)
+      let day = (date.getDate() >= 10) ? date.getDate() : '0' + date.getDate()
+      let hours = (date.getHours() >= 10) ? date.getHours() : '0' + date.getHours()
+      let minutes = (date.getMinutes() >= 10) ? date.getMinutes() : '0' + date.getMinutes()
+      let seconds = (date.getSeconds() >= 10) ? date.getSeconds() : '0' + date.getSeconds()
+      if (value) {
+        return years + '-' + month + '-' + day +
+          ' ' + hours + ':' + minutes + ':' + seconds
+      } else {
+        return 'N/A'
+      }
+    }
+  },
+  methods: {
+    init (info) {
+      this.visible = true
+      this.info = info
+      this.dataList = []
+      this.pageIndex = 1
+      this.pageSize = 10
+      this.totalPage = 0
+      // console.log(info.id)
+      this.getJobLog()
+    },
+    getJobLog () {
+      // console.log(id)
+      this.dataList = []
+      this.logDataListLoading = true
+      this.$http({
+        url: this.$http.snoopUrl('/v1/jobs/instances'),
+        methods: 'get',
+        params: this.$http.adornParams({
+          'page': this.pageIndex - 1,
+          'size': this.pageSize + 1,
+          'jobId': this.info.id
+        })
+      }).then((data) => {
+        if (data && data.status === 200) {
+          console.log(data.data)
+          this.dataList = data.data
+        }
+        this.$nextTick(() => {
+          this.haveNextPage()
+        })
+        this.logDataListLoading = false
+      })
+      // this.dataList = [
+      //   {
+      //     'id': 1727,
+      //     'sessionId': 719,
+      //     'state': 'STARTING',
+      //     'type': 'BATCH',
+      //     'appId': 'application_1642725586357_0719',
+      //     'appUri': 'http://griffin:8088 /cluster/app/ application_1642725586357_0719',
+      //     'predicateGroup': 'PG',
+      //     'predicateName': 'demo1_job_predicate_1642734240026',
+      //     'timestamp': 1642734240026,
+      //     'expireTimestamp': 1643339040026
+      //   },
+      //   {
+      //     'id': 1721,
+      //     'sessionId': 713,
+      //     'state': 'STARTING',
+      //     'type': 'BATCH',
+      //     'appId': 'application_1642725586357_0714',
+      //     'appUri': 'http://griffin:8088 /cluster/app/ application_1642725586357_0714',
+      //     'predicateGroup': 'PG',
+      //     'predicateName': 'demo1_job_predicate_1642734180011',
+      //     'timestamp': 1642734180012,
+      //     'expireTimestamp': 1643338980012
+      //   }]
+    },
+    detailData (url) {
+      let newuRL = url.replace(/hadoop112/g, 'hadoop113')
+      window.open(newuRL, '_blank')
+    },
+    // 每页数
+    sizeChangeHandle (val) {
+      this.pageSize = val
+      this.pageIndex = 1
+      this.getJobLog()
+    },
+    // 当前页
+    currentChangeHandle (val) {
+      this.pageIndex = val
+      this.getJobLog()
+    },
+    haveNextPage () {
+      if (this.dataList.length > this.pageSize) {
+        this.totalPage = (this.pageIndex - 1) * this.pageSize + this.dataList.length
+        this.dataList.pop()
+      }
+    }
+
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 207 - 0
src/views/modules/snoop/jobResult.vue

@@ -0,0 +1,207 @@
+<template>
+  <div>
+    <el-dialog
+      :title="title"
+      :close-on-click-modal="false"
+      v-if="visible"
+      :visible.sync="visible"
+      width="75%">
+      <el-table
+        :data="useDataList"
+        border
+        v-loading="dataListLoading"
+        height="530"
+        style="width: 100%;margin-top: -30px;">
+
+        <el-table-column v-if="showID" prop="Id" header-align="center" align="center" width="80" label="id"
+                         type="index" :index='(index)=>{return (index+1) + (this.pageIndex-1)*this.pageSize}'>
+        </el-table-column>
+        <el-table-column  v-if="showID" header-align="center" align="center"  label="time">
+          <template slot-scope="scope">
+            {{scope.row.time | timeFormater}}
+          </template>
+        </el-table-column>
+        <el-table-column v-for="col in tableClo" :key="col"
+                         :prop="col"
+                         header-align="center"
+                         align="center"
+                         :label="col">
+        </el-table-column>
+      </el-table>
+      <el-pagination
+        @size-change="sizeChangeHandle"
+        @current-change="currentChangeHandle"
+        :current-page="pageIndex"
+        :page-sizes="[10, 20, 50, 100]"
+        :page-size="pageSize"
+        :total="totalPage"
+        layout=" sizes, prev, pager, next">
+      </el-pagination>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'jobResult',
+  data () {
+    return {
+      dataList: [],
+      useDataList: [],
+      tableClo: [],
+      visible: true,
+      info: null,
+      showID: true,
+      title: '',
+      pageIndex: 1,
+      pageSize: 10,
+      totalPage: 0,
+      dataListLoading: false
+    }
+  },
+  filters: {
+    timeFormater (value) {
+      // console.log('@', value)
+      var date = new Date(value)
+      let years = date.getFullYear()
+      let month = (date.getMonth() + 1) > 10 ? date.getMonth() + 1 : '0' + (date.getMonth() + 1)
+      let day = (date.getDate() >= 10) ? date.getDate() : '0' + date.getDate()
+      let hours = (date.getHours() >= 10) ? date.getHours() : '0' + date.getHours()
+      let minutes = (date.getMinutes() >= 10) ? date.getMinutes() : '0' + date.getMinutes()
+      let seconds = (date.getSeconds() >= 10) ? date.getSeconds() : '0' + date.getSeconds()
+      if (value) {
+        return years + '-' + month + '-' + day +
+          ' ' + hours + ':' + minutes + ':' + seconds
+      } else {
+        return 'N/A'
+      }
+    }
+  },
+  methods: {
+    init (info) {
+      this.visible = true
+      this.info = info
+      this.title = '结果 : ' + info.jobName
+      this.dataList = []
+      this.useDataList = []
+      this.getJobRes()
+      this.pageIndex = 1
+      this.pageSize = 10
+      this.totalPage = 0
+      // console.log(info)
+    },
+    getJobRes () {
+      this.dataListLoading = true
+      this.useDataList = []
+      this.$http({
+        url: this.$http.snoopUrl('/v1/metrics/values'),
+        methods: 'get',
+        params: this.$http.adornParams({
+          'offset': (this.pageIndex - 1) * this.pageSize,
+          'size': this.pageSize + 1,
+          'metricName': this.info.jobName
+        })
+      }).then((data) => {
+        if (data.data.length && data.status === 200) {
+          this.showID = true
+          this.dataList = data.data
+          for (const item of this.dataList) {
+            item.value['time'] = item.tmst
+            for (let key in item.value) {
+              if (typeof (item.value[key]) !== 'object') {
+                item.value[key].toString()
+              } else {
+                const keysplit = key.split('-')
+                let records = ''
+                let record
+                for (const i in item.value[key]) {
+                  let name, count
+                  for (const category in item.value[key][i]) {
+                    if (category !== 'count') {
+                      name = item.value[key][i][category]
+                      count = item.value[key][i].count
+                    }
+                  }
+                  record = ' (' + name + ',' + count + ') '
+                  records += record
+                }
+                delete item.value[key]
+                key = key + ' (' + keysplit[0].split('_')[0] + ', count)'
+                item.value[key] = records
+              }
+            }
+            this.useDataList.push(item.value)
+          }
+          // console.log(this.dataList)
+          // this.useDataList = this.dataList
+          // this.dataList.forEach((x) => {
+          //   console.log(x)
+          //   this.useDataList.push(x.value)
+          // })
+          // // console.log(this.dataList[0].value)
+          this.tableClo = Object.keys(this.dataList[0].value)
+          this.tableClo.forEach((data, index) => {
+            if (data === 'time') {
+              this.tableClo.splice(index, 1)
+            }
+          })
+          this.dataListLoading = false
+          this.$nextTick(() => {
+            this.haveNextPage()
+          })
+          // console.log(this.tableClo)
+        } else {
+          this.tableClo = []
+          this.showID = false
+          this.dataListLoading = false
+        }
+      })
+      // this.dataList = [
+      //   {
+      //     'name': 'demo3',
+      //     'tmst': 1642670565000,
+      //     'value': {
+      //       'id_count': 6375000,
+      //       'id_max': 124,
+      //       'age_max': 2000,
+      //       'age_min': 1
+      //     }
+      //   },
+      //   {
+      //     'name': 'demo3',
+      //     'tmst': 1642670550000,
+      //     'value': {
+      //       'id_count': 6375000,
+      //       'id_max': 124,
+      //       'age_max': 2000,
+      //       'age_min': 1
+      //     }
+      //   }]
+    },
+    // 每页数
+    sizeChangeHandle (val) {
+      this.pageSize = val
+      this.pageIndex = 1
+      this.getJobRes()
+    },
+    // 当前页
+    currentChangeHandle (val) {
+      this.pageIndex = val
+      this.getJobRes()
+    },
+    /** 判断是否还有下一页
+     *  思路 : 每次请求 pageSize +1 判断实际收到的数组长度
+     */
+    haveNextPage () {
+      if (this.useDataList.length > this.pageSize) {
+        this.totalPage = (this.pageIndex - 1) * this.pageSize + this.useDataList.length
+        this.useDataList.pop()
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 194 - 0
src/views/modules/snoop/jobResult_visi.vue

@@ -0,0 +1,194 @@
+<template>
+  <div>
+    <el-dialog
+      title= '结果 : '
+      :close-on-click-modal="false"
+      v-if="visible"
+      :visible.sync="visible"
+      width="75%">
+      <div  v-loading="dataListLoading" >
+        <div id = 'showResult'></div>
+      </div>
+      <div style="text-align: center">
+        <span style="">{{title  }} :    </span><el-select v-model="selectValue" placeholder="请选择" size="mini">
+        <el-option
+          v-for="item in tableClo"
+          :key="item"
+          :label="item"
+          :value="item">
+        </el-option>
+      </el-select>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// 引入 AutV G2 npm install @antv/g2
+import { Chart } from '@antv/g2'
+
+export default {
+  name: 'jobResult',
+  data () {
+    return {
+      dataList: [],
+      visiDataMap: {},
+      tableClo: [],
+      visible: true,
+      info: null,
+      chart: null,
+      title: '',
+      selectValue: '',
+      dataListLoading: false
+    }
+  },
+  watch: {
+    selectValue:
+    {
+      immediate: false, // 初始化时让handler调用一下
+      handler (newValue, oldValue) {
+        this.dataListLoading = true
+        console.log(this.selectValue)
+        this.chart.data(this.visiDataMap[this.selectValue])
+        this.chart.render()
+        this.dataListLoading = false
+      }
+    }
+  },
+  methods: {
+    init (info) {
+      this.visible = true
+      this.info = info
+      this.title = info.jobName
+      this.dataList = []
+      this.tableClo = []
+      this.visiDataMap = {}
+      this.chart = null
+      this.getJobRes()
+      // console.log(info)
+    },
+    getJobRes () {
+      this.dataListLoading = true
+      this.visiDataMap = {}
+      var visiData = {}
+      this.$http({
+        url: this.$http.snoopUrl('/v1/metrics/values'),
+        methods: 'get',
+        params: this.$http.adornParams({
+          'offset': 0,
+          'size': 100,
+          'metricName': this.info.jobName
+        })
+      }).then((data) => {
+        if (data.data.length && data.status === 200) {
+          this.dataList = data.data
+          let fristSign = true
+          for (const item of this.dataList) {
+            let time = this.timeFormater(item.tmst)
+            for (let key in item.value) {
+              if (fristSign) {
+                this.visiDataMap[key] = []
+              }
+              if (typeof (item.value[key]) !== 'object') {
+                visiData = {
+                  'time': time,
+                  'value': item.value[key].toString()
+                }
+                this.visiDataMap[key].push(visiData)
+              } else {
+                for (const i in item.value[key]) {
+                  let name, count
+                  for (const category in item.value[key][i]) {
+                    if (category !== 'count') {
+                      name = item.value[key][i][category]
+                      count = item.value[key][i].count
+                    }
+                  }
+                  visiData = {
+                    'time': time,
+                    'name': name,
+                    'value': count
+                  }
+
+                  this.visiDataMap[key].push(visiData)
+                }
+              }
+            }
+            fristSign = false
+          }
+          this.tableClo = Object.keys(this.dataList[0].value)
+          this.selectValue = this.tableClo[0]
+
+          this.charInit(this.visiDataMap[this.selectValue])
+          this.dataListLoading = false
+          // console.log(this.tableClo)
+        } else {
+          this.tableClo = []
+          this.dataListLoading = false
+        }
+      })
+    },
+    timeFormater (value) {
+      // console.log('@', value)
+      var date = new Date(value)
+      // let years = date.getFullYear()
+      // let month = (date.getMonth() + 1) > 10 ? date.getMonth() + 1 : '0' + (date.getMonth() + 1)
+      // let day = (date.getDate() >= 10) ? date.getDate() : '0' + date.getDate()
+      let hours = (date.getHours() >= 10) ? date.getHours() : '0' + date.getHours()
+      let minutes = (date.getMinutes() >= 10) ? date.getMinutes() : '0' + date.getMinutes()
+      let seconds = (date.getSeconds() >= 10) ? date.getSeconds() : '0' + date.getSeconds()
+      if (value) {
+        // return years + '-' + month + '-' + day + '' + hours + ':' + minutes + ':' + seconds
+        return hours + ':' + minutes + ':' + seconds
+      } else {
+        return 'N/A'
+      }
+    },
+    charInit (data) {
+      console.log(this.selectValue)
+      this.chart = new Chart({
+        container: 'showResult',
+        autoFit: true,
+        height: 500,
+        padding: [50, 20, 50, 20]
+      })
+      // data.forEach(x => {
+      //   x['value'] = x['value'] / 100000
+      // })
+      this.chart.data(data)
+      this.chart.scale({
+        time: {
+          tickCount: 10
+        },
+        value: {
+          nice: true
+        }
+      })
+      this.chart.scale({
+        time: {
+          tickCount: 10
+        },
+        value: {
+          nice: true,
+          tickCount: 10
+        }
+      })
+      this.chart.axis('value', {
+        tickLine: null,
+        label: {
+          autoRotate: false,
+          autoHide: false // 取消自动隐藏label
+        }
+      })
+      this.chart.line().position('time*value')
+
+      this.chart.render()
+      // this.chart.interval().position('time*value')
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 298 - 0
src/views/modules/snoop/jobResult_visi_echars.vue

@@ -0,0 +1,298 @@
+<template>
+  <div>
+    <el-dialog
+      :close-on-click-modal="false"
+      v-if="visible"
+      :visible.sync="visible"
+      width="75%"
+      destroy-on-close>
+      <div  v-loading="dataListLoading" >
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-card>
+              <div v-show="!showCiyun" id="jobresultshow" style="min-height: 400px;">
+                暂无数据
+              </div>
+              <div  v-show="showCiyun" id="jobresultshowCiYun" style="min-height: 400px;">
+                暂无数据
+              </div>
+            </el-card>
+          </el-col>
+          <el-col :span="24">
+              <div style="text-align: center;margin-top: 20px">
+                <span style="">{{title  }} :    </span><el-select v-model="selectValue" placeholder="请选择" size="mini">
+                <el-option
+                  v-for="item in tableClo"
+                  :key="item"
+                  :label="item"
+                  :value="item">
+                </el-option>
+              </el-select>
+              </div>
+          </el-col>
+        </el-row>
+      </div>
+
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import echarts from 'echarts'
+// cnpm install echarts-wordcloud@1.1.3 --save
+import 'echarts-wordcloud/dist/echarts-wordcloud.min'
+export default {
+  name: 'jobResult',
+  data () {
+    return {
+      dataList: [],
+      visiDataMap: {},
+      tableClo: [],
+      visible: true,
+      info: null,
+      title: '',
+      selectValue: '-1',
+      dataListLoading: false,
+      showCiyun: false,
+      chartLine: null,
+      charWordCloud: null
+    }
+  },
+  watch: {
+    selectValue:
+    {
+      immediate: false, // 初始化时让handler调用一下
+      handler (newValue, oldValue) {
+        if (oldValue !== '-1') {
+          this.dataListLoading = true
+          this.charDecisionTree(newValue)
+          this.dataListLoading = false
+        }
+      }
+    }
+  },
+  methods: {
+    init (info) {
+      this.visible = true
+      this.info = info
+      this.title = info.jobName
+      this.dataList = []
+      this.tableClo = []
+      this.visiDataMap = {}
+      this.getJobRes()
+      // console.log(info)
+    },
+    getJobRes () {
+      this.dataListLoading = true
+      this.visiDataMap = {}
+      var visiData = {}
+      this.$http({
+        url: this.$http.snoopUrl('/v1/metrics/values'),
+        methods: 'get',
+        params: this.$http.adornParams({
+          'offset': 0,
+          'size': 100,
+          'metricName': this.info.jobName
+        })
+      }).then((data) => {
+        if (data.data.length && data.status === 200) {
+          this.dataList = data.data
+          let fristSign = true
+          for (const item of this.dataList) {
+            let time = this.timeFormater(item.tmst)
+            for (let key in item.value) {
+              if (fristSign) {
+                this.visiDataMap[key] = []
+              }
+              if (typeof (item.value[key]) !== 'object') {
+                visiData = {
+                  'time': time,
+                  'value': item.value[key].toString()
+                }
+                this.visiDataMap[key].push(visiData)
+              } else {
+                for (const i in item.value[key]) {
+                  let name, count
+                  for (const category in item.value[key][i]) {
+                    if (category !== 'count') {
+                      name = item.value[key][i][category]
+                      count = item.value[key][i].count
+                    }
+                  }
+                  visiData = {
+                    'time': time,
+                    'name': name,
+                    'value': count
+                  }
+
+                  this.visiDataMap[key].push(visiData)
+                }
+              }
+            }
+            fristSign = false
+          }
+          this.tableClo = Object.keys(this.dataList[0].value)
+          this.selectValue = this.tableClo[0]
+          this.dataListLoading = false
+          // console.log(this.tableClo)
+        } else {
+          this.tableClo = []
+          this.dataListLoading = false
+        }
+        this.$nextTick(() => {
+          this.charDecisionTree(this.selectValue)
+        })
+      })
+    },
+    timeFormater (value) {
+      // console.log('@', value)
+      var date = new Date(value)
+      // let years = date.getFullYear()
+      // let month = (date.getMonth() + 1) > 10 ? date.getMonth() + 1 : '0' + (date.getMonth() + 1)
+      // let day = (date.getDate() >= 10) ? date.getDate() : '0' + date.getDate()
+      let hours = (date.getHours() >= 10) ? date.getHours() : '0' + date.getHours()
+      let minutes = (date.getMinutes() >= 10) ? date.getMinutes() : '0' + date.getMinutes()
+      let seconds = (date.getSeconds() >= 10) ? date.getSeconds() : '0' + date.getSeconds()
+      if (value) {
+        // return years + '-' + month + '-' + day + '' + hours + ':' + minutes + ':' + seconds
+        return hours + ':' + minutes + ':' + seconds
+      } else {
+        return 'N/A'
+      }
+    },
+    charDecisionTree (cloum) {
+      if (cloum.search('top10') === -1) {
+        this.showCiyun = false
+        this.initChartLine(cloum)
+      } else {
+        this.showCiyun = true
+        this.$nextTick(() => {
+          this.initCharWordCloud(cloum)
+        })
+      }
+    },
+    // 折线图
+    initChartLine (cloum) {
+      let datas = this.visiDataMap[cloum]
+      console.log(datas)
+      if (datas && cloum !== '-1') {
+        let xdata = []
+        let ydata = []
+        datas.map((data) => {
+          xdata.push(data['time'])
+          ydata.push(data['value'])
+        })
+        var option = {
+          title: {
+            text: this.title + ':' + this.selectValue
+          },
+          tooltip: {
+            trigger: 'axis'
+          },
+          // legend: {
+          //   data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+          // },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+          },
+          toolbox: {
+            feature: {
+              saveAsImage: {}
+            }
+          },
+          xAxis: {
+            type: 'category',
+            boundaryGap: false,
+            data: xdata.reverse()
+          },
+          yAxis: {
+            type: 'value'
+          },
+          series: [
+            {
+              name: 'value',
+              type: 'line',
+              stack: '总量',
+              data: ydata.reverse()
+            }
+          ]
+        }
+        this.chartLine = echarts.init(document.getElementById('jobresultshow'))
+        this.chartLine.setOption(option)
+        // window.addEventListener('resize', () => {
+        //   this.chartLine.resize()
+        // })
+      }
+    },
+    // 词云
+    initCharWordCloud (cloum) {
+      let datas = this.visiDataMap[cloum]
+      if (datas && cloum !== '-1') {
+        var option = {
+          title: {
+            text: this.title + ':' + this.selectValue
+          },
+          tooltip: {
+            show: true,
+            position: 'top',
+            textStyle: {
+              fontSize: 30
+            }
+          },
+          series: [{
+            type: 'wordCloud',
+           // 用来调整词之间的距离
+            gridSize: 30,
+           // 用来调整字的大小范围
+
+            sizeRange: [14, 60],
+           // 用来调整词的旋转方向,,[0,0]--代表着没有角度,也就是词为水平方向,需要设置角度参考注释内容
+           // rotationRange: [-45, 0, 45, 90],
+           // rotationRange: [ 0,90],
+            rotationRange: [0, 0],
+           // 随机生成字体颜色
+            textStyle: {
+              normal: {
+                color: function () {
+                  return (
+                   'rgb(' +
+                   Math.round(Math.random() * 255) +
+                   ', ' +
+                   Math.round(Math.random() * 255) +
+                   ', ' +
+                   Math.round(Math.random() * 255) +
+                   ')'
+                  )
+                }
+              }
+            },
+           // 位置相关设置
+            left: 'center',
+            top: 'center',
+            right: null,
+            bottom: null,
+            width: '200%',
+            height: '200%',
+           // 数据
+            data: datas.slice(0, 10)
+          }]
+        }
+        this.charWordCloud = echarts.init(document.getElementById('jobresultshowCiYun'))
+        this.charWordCloud.setOption(option)
+      }
+
+      // window.addEventListener('resize', () => {
+      //   this.charWordCloud.resize()
+      // })
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 106 - 0
src/views/modules/snoop/jobWatch.vue

@@ -0,0 +1,106 @@
+<template>
+  <div>
+
+
+
+    <el-dialog :title="'查看任务'" :close-on-click-modal="false" :visible.sync="visible">
+
+      <el-form :model="job_info" label-width="200px"  v-loading="jobDataListLoading">
+        <el-form-item label="任务名称 :" prop="name">
+          <!--                  <el-input v-model="goal_info.config.name"></el-input>-->
+          <span>{{ job_info.jobName }}</span>
+        </el-form-item>
+        <el-form-item label="度量名称 :">
+          <span>{{ job_info.measureName }}</span>
+          <!--                  <el-input v-model="goal_info.config.description"></el-input>-->
+        </el-form-item>
+        <el-form-item label="度量类型 :">
+          <span>{{ job_info.measureType }}</span>
+        </el-form-item>
+
+        <el-form-item label="处理方式 :">
+          <span>{{ job_info.processType }}</span>
+        </el-form-item>
+        <el-form-item label="探测周期 :">
+          <span>{{ job_info.cronExpression }}</span>
+      </el-form-item>
+        <el-form-item label="时  区 :">
+          <span>{{ job_info.cronTimeZone }}</span>
+        </el-form-item>
+        <el-form-item label="单次探测起始时间 :">
+          <span>{{ job_info.rangeConfig.begin}}</span>
+      </el-form-item>
+        <el-form-item label="单次探测持续时间 :">
+          <span>{{ job_info.rangeConfig.length }}</span>
+        </el-form-item>
+      </el-form>
+
+
+      <span slot="footer" class="dialog-footer">
+      <el-button @click="visible = false">取消</el-button>
+      <el-button type="primary" @click="visible=false">确定</el-button>
+    </span>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'jobWatch',
+  data () {
+    return {
+      job_info: {
+        jobName: '',
+        measureName: '',
+        measureType: '',
+        processType: '',
+        cronExpression: '',
+        cronTimeZone: '',
+        rangeConfig: ''
+      },
+      visible: false,
+      jobDataListLoading: false
+    }
+  },
+  methods: {
+    init (info) {
+      this.visible = true
+      this.jobDataListLoading = false
+      this.getJobInfo(info)
+      // console.log(info)
+    },
+    getJobInfo (info) {
+      this.job_info = info
+      const mesureId = info['measureId']
+      this.getMeasureById(mesureId)
+      for (const item of info['dataSegments']) {
+        // console.log(item['segment.range'])
+        this.job_info.rangeConfig = item['segment.range']
+      }
+    },
+    getMeasureById (id) {
+      this.jobDataListLoading = true
+      this.$http({
+        url: this.$http.snoopUrl('/v1/measures/') + id,
+        methods: 'get'
+      }).then((data) => {
+        if (data && data.status === 200) {
+          var measureData = data.data
+          this.job_info.measureName = measureData.name
+          this.job_info.measureType = measureData['dq.type'].toLowerCase()
+          this.job_info.processType = measureData['process.type'].toLowerCase()
+        }
+        this.jobDataListLoading = false
+      }
+
+        )
+    }
+
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 953 - 0
src/views/modules/snoop/measure.vue

@@ -0,0 +1,953 @@
+<!--  -->
+<template>
+  <div class=''>
+    <el-breadcrumb class="divi2" separator-class="el-icon-arrow-right">
+      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
+      <el-breadcrumb-item :to="{ path: '/snoop-home'}">数据质量探测</el-breadcrumb-item>
+      <el-breadcrumb-item>创建度量标准</el-breadcrumb-item>
+    </el-breadcrumb>
+    <el-divider class="divi"></el-divider>
+
+    <el-steps :active="active" align-center finish-status="success" style="margin: 50px 20px 0px 10px;">
+      <el-step title="选择目标" description="选择需要度量的目标数据集字段"></el-step>
+      <el-step title="定义/选择模型" description="定义将应用于所选字段的语法检查逻辑"></el-step>
+      <el-step title="分区配置" description="设置目标数据集的分区配置"></el-step>
+      <el-step title="完成" description="设置模型的基本配置"></el-step>
+    </el-steps>
+
+
+    <div style=" margin: 10px 120px 0px 166px;height: 420px;">
+      <el-row :gutter="50">
+        <div v-if="active == 0">
+          <el-col :span="8" style="height: 100% ">
+            <h4>1.1 选择数据源</h4>
+            <div style="height: 360px;border: 1px solid rgb(0, 0, 0);overflow: scroll; ">
+              <div style="margin: 10px;"> <!-- 控制左边树形区域的下方的div -->
+                <el-tree
+                  v-if="showTree"
+                  id='tree'
+                  node-key="subEquipmentid"
+                  :props="defaultProps"
+                  ref="tree"
+                  :load="loadTreeNode"
+                  lazy
+                  @node-click="treeNodeClick">
+                <span class="custom-tree-node" slot-scope="{ node, data }">
+                  <span>
+                      <i :class="data.icon" style="margin-right: 4px"></i>{{ node.label }}
+                  </span>
+               </span>
+                </el-tree>
+              </div>
+            </div>
+          </el-col>
+          <el-col :span="16" style="height: 100%">
+            <h4>1.2 选择字段</h4>
+            <div style="height: 360px;border: 1px solid rgb(0, 0, 0) ;padding: 10px;overflow-scrolling: auto">
+              <el-table
+                ref="multipleTable"
+                :data="tableData"
+                tooltip-effect="dark"
+                style="width: 100%"
+                @selection-change="handleSelectionChange">
+                <el-table-column
+                  type="selection"
+                  width="55">
+                </el-table-column>
+                <el-table-column
+                  prop="colname"
+                  label="列名"
+                  width="120">
+                </el-table-column>
+                <el-table-column
+                  prop="type"
+                  label="类型"
+                  width="120">
+                </el-table-column>
+                <el-table-column
+                  prop="comment"
+                  label="备注"
+                  show-overflow-tooltip>
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-col>
+        </div>
+
+        <div v-if="active == 1">
+          <el-col :span="16" style="height: 100%;margin-left: 160px;">
+            <h4>2. 请选择下列其中一种分析模式 :</h4>
+            <div style="height: 360px;border: 1px solid rgb(0, 0, 0) ;padding: 10px;overflow-scrolling: auto">
+              <el-table
+                :data="goal_info.chooseCloum"
+                style="width: 100%">
+                <el-table-column prop="colname" label="列名" width="120"></el-table-column>
+                <el-table-column prop="type" label="类型" width="120"></el-table-column>
+                <el-table-column prop="comment" label="rule" show-overflow-tooltip>
+                  <template slot="header" slot-scope="scope">
+                    <span>rule</span>
+                    <i class="el-icon-info"
+                       :class="isShowRules"
+                       @mouseover="isShowRules.showRules = true"
+                       @mouseleave="isShowRules.showRules = false"
+                       @click="showrule"
+                    ></i>
+                  </template>
+
+<!--                  <template slot="header">-->
+
+<!--                    <i class="el-icon-info"-->
+<!--                       @mouseover="isShowRules.showRules = true"-->
+<!--                       @mouseleave="isShowRules.showRules = false"-->
+<!--                       @click="showrule"></i>-->
+<!--                  </template>-->
+
+                  <template slot-scope="scope" style="width: 80%">
+                    <!--                                        <el-button type="text" size="small" @click="userwatch(scope)">查看-->
+                    <!--                                        </el-button>-->
+
+                    <el-select
+                      v-model="scope.row.rule"
+                      multiple
+                      filterable
+                      style="width: 80%"
+                      placeholder="请选择">
+                      <el-option-group
+                        v-for="group in scope.row.dropdownList"
+                        :key="group.label"
+                        :label="group.label"
+
+                      >
+                        <el-option
+                          v-for="item in group.options"
+                          :key="item.value"
+                          :label="item.label"
+                          :value="item.value">
+                        </el-option>
+                      </el-option-group>
+
+                    </el-select>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-col>
+        </div>
+
+        <div v-if="active == 2">
+          <el-col :span="16" style="height: 100%;margin-left: 160px;">
+            <h4>3. 配置信息 :</h4>
+            <div style="height: 360px;overflow-scrolling: auto">
+              <h5>度量配置 : </h5>
+              <el-form ref="form" :model="datacForm" label-width="100px" :rules="rules">
+                <el-form-item label="度量名称 :" prop="name">
+                  <el-input v-model="datacForm.name"></el-input>
+                </el-form-item>
+                <el-form-item label="度量描述 :">
+                  <el-input v-model="datacForm.description"></el-input>
+                </el-form-item>
+
+                <h5>数据源配置 : </h5>
+                <el-form-item label="查询条件 :">
+                  <el-input v-model="datacForm.where"></el-input>
+                </el-form-item>
+                <el-form-item label="分区大小 :">
+                  <el-input-number v-model="datacForm.num" controls-position="right" :min="0"></el-input-number>
+                  <el-select v-model="datacForm.timetype" placeholder="请选择">
+                    <el-option label="day" value="day"></el-option>
+                    <el-option label="hour" value="hour"></el-option>
+                    <el-option label="minute" value="minute"></el-option>
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="时 区 :">
+                  <el-select v-model="datacForm.timezone" placeholder="请选择">
+                    <el-option
+                      v-for="item in timezones"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value">
+                    </el-option>
+                  </el-select>
+
+                </el-form-item>
+              </el-form>
+            </div>
+          </el-col>
+        </div>
+
+        <div v-if="active == 3">
+          <el-col :span="16" style="height: 100%;margin-left: 160px;">
+            <h4>4. 信息校对 :</h4>
+            <div style="height: 360px;overflow-scrolling: auto">
+
+              <el-form ref="measureform" :model="goal_info" label-width="100px" :rules="rules">
+                <el-form-item label="度量名称 :" prop="name">
+                  <!--                  <el-input v-model="goal_info.config.name"></el-input>-->
+                  <span>{{ goal_info.config.name }}</span>
+                </el-form-item>
+                <el-form-item label="度量描述 :">
+                  <span>{{ goal_info.config.description }}</span>
+                  <!--                  <el-input v-model="goal_info.config.description"></el-input>-->
+                </el-form-item>
+                <el-form-item label="数据源 :">
+                  <span>{{ goal_info.equipment.name }}</span>
+                  <!--                  <el-input v-model="goal_info.equipment.name"></el-input>-->
+                </el-form-item>
+                <!--                <el-form-item label="查询条件 :">-->
+                <!--                  <span>{{goal_info.config.where}}</span>-->
+                <!--&lt;!&ndash;                  <el-input v-model="goal_info.config.where"></el-input>&ndash;&gt;-->
+                <!--                </el-form-item>-->
+                                <el-form-item label="分区信息 :">
+                                  <span>{{goal_info.config.num  + "  " + goal_info.config.timetype + " " + goal_info.config.timezone}}</span>
+<!--                                  <el-input v-model="goal_info.config.num" >{{goal_info.config.num + goal_info.config.timetype}}</el-input>-->
+<!--                                  <el-input v-model="goal_info.config.timezone"></el-input>-->
+                                </el-form-item>
+                <el-form-item label="度量规则 :">
+                  <!--                  <el-input-number v-model="datacForm.num" controls-position="right" :min="0"></el-input-number>-->
+                  <div style="height: 100px;overflow: auto">
+                    <template v-for="rule in  goal_info.chooseCloum">
+                      <li>{{ rule.colname }} : {{ rule.rule }}</li>
+                    </template>
+
+                    <!--    <template v-for="rule in  timezones" >-->
+                    <!--      <li>{{rule.label}} : {{rule.value}}</li>-->
+                    <!--    </template>-->
+                  </div>
+                </el-form-item>
+              </el-form>
+            </div>
+          </el-col>
+        </div>
+      </el-row>
+
+
+    </div>
+    <el-form :inline="true">
+      <el-form-item style="float:left">
+        <el-button type="primary" @click="preStey()" :disabled = bottonDisable.pre>上一步</el-button>
+      </el-form-item>
+      <el-form-item style="float:right">
+        <el-button v-if="!buttonFinish" ref="buttonFinish" type="primary" @click="next()" :disabled = bottonDisable.next>下一步</el-button>
+        <el-button v-if="buttonFinish" type="primary" @click="submitForm()" >完成</el-button>
+      </el-form-item>
+    </el-form>
+
+    <show-rules v-if="isShowRulesDrog" ref="ShowRulesDrogref"></show-rules>
+  </div>
+</template>
+
+<script>
+// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
+// 例如:import 《组件名称》 from '《组件路径》';
+import showRules from './showRules'
+
+export default {
+  created () {
+
+  },
+  components: {
+    showRules
+  },
+  data () {
+    return {
+      value: [],
+      showTree: true,
+      visible: false,
+      chooseEqpment: '',
+      active: 0,
+      goal_info: '',
+      userID: this.$store.state.user.id, // 用户编号
+      // userName: this.$store.state.user.name, // 用户编号
+      userName: 'test', // 用户编号
+      isShowRules: {
+        showRules: false
+      },
+
+      transrule: [],
+      transenumrule: [],
+      transnullrule: [],
+      transregexrule: [],
+      newMeasure: {},
+      rulesDesc: [],
+
+      buttonFinish: false,
+      bottonDisable: {
+        pre: true,
+        next: true
+      },
+      rules: {
+        name: [
+          {required: true, message: '请输入度量名称', trigger: 'blur'},
+          {min: 3, max: 18, message: '长度在 3 到 18 个字符', trigger: 'blur'}
+        ]
+      },
+      isShowRulesDrog: false,
+      datacForm: {
+        name: '',
+        description: '',
+        where: '',
+        timezone: '',
+        num: 1,
+        timetype: 'day',
+        needpath: false,
+        path: ''
+      },
+      options_int: [{
+        label: 'Simple Statistics',
+        options: [{
+          id: 1,
+          value: 'Null Count',
+          label: 'Null Count'
+        }, {
+          id: 2,
+          value: 'Distinct Count',
+          label: 'Distinct Count'
+        }]
+      }, {
+        label: 'Summary Statistics',
+        options: [{
+          id: 3,
+          value: 'Total Count',
+          label: 'Total Count'
+        }, {
+          id: 4,
+          value: 'Maximum',
+          label: 'Maximum'
+        }, {
+          id: 5,
+          value: 'Minimum',
+          label: 'Minimum'
+        }, {
+          id: 6,
+          value: 'Average',
+          label: 'Average'
+        }]
+      }, {
+        label: 'Advanced Statistics',
+        options: [{
+          id: 7,
+          value: 'Enum Detection Top10 Count',
+          label: 'Enum Detection Top10 Count'
+        }]
+      }
+      ],
+      options_str: [{
+        label: 'Simple Statistics',
+        options: [{
+          id: 1,
+          value: 'Null Count',
+          label: 'Null Count'
+        }, {
+          id: 2,
+          value: 'Distinct Count',
+          label: 'Distinct Count'
+        }]
+      }, {
+        label: 'Summary Statistics',
+        options: [{
+          id: 3,
+          value: 'Total Count',
+          label: 'Total Count'
+        }]
+      }, {
+        label: 'Advanced Statistics',
+        options: [{
+          id: 9,
+          value: 'Enum Detection Top10 Count',
+          label: 'Enum Detection Top10 Count'
+        }]
+      }
+      ],
+      treedata: [{ // 左侧默认树数据'
+        icon: 'el-icon-success',
+        name: '煤矿',
+        limit: '',
+        children: [{
+          name: '系统',
+          limit: '',
+          children: [{
+            name: '子系统',
+            limit: '',
+            children: [{
+              name: '设备',
+              limit: '',
+              children: [{
+
+                name: '子设备',
+                limit: ''
+              }]
+            }]
+          }]
+        }]
+      }, {
+        name: '系统666',
+        limit: '',
+        children: [{
+          name: '设备2',
+          limit: ''
+        }]
+      }],
+      defaultProps: {
+        children: 'children',
+        label: 'name',
+        isLeaf: 'leaf'
+      },
+      tableData: [],
+      timezones: [
+        {label: 'UTC-12(IDL)', value: 'GMT-12'},
+        {label: 'UTC-11(MIT)', value: 'GMT-11'},
+        {label: 'UTC-10(HST)', value: 'GMT-10'},
+        {label: 'UTC-9:30(MSIT)', value: 'GMT-9:30'},
+        {label: 'UTC-9(AKST)', value: 'GMT-9'},
+        {label: 'UTC-8(PST)', value: 'GMT-8'},
+        {label: 'UTC-7(MST)', value: 'GMT-7'},
+        {label: 'UTC-6(CST)', value: 'GMT-6'},
+        {label: 'UTC-5(EST)', value: 'GMT-5'},
+        {label: 'UTC-4(AST)', value: 'GMT-4'},
+        {label: 'UTC-3:30(NST)', value: 'GMT-3:30'},
+        {label: 'UTC-3(SAT)', value: 'GMT-3'},
+        {label: 'UTC-2(BRT)', value: 'GMT-2'},
+        {label: 'UTC-1(CVT)', value: 'GMT-1'},
+        {label: 'UTC(WET,GMT)', value: 'GMT'},
+        {label: 'UTC+1(CET)', value: 'GMT+1'},
+        {label: 'UTC+2(EET)', value: 'GMT+2'},
+        {label: 'UTC+3(MSK)', value: 'GMT+3'},
+        {label: 'UTC+3:30(IRT)', value: 'GMT+3:30'},
+        {label: 'UTC+4(META)', value: 'GMT+4'},
+        {label: 'UTC+4:30(AFT)', value: 'GMT+4:30'},
+        {label: 'UTC+5(METB)', value: 'GMT+5'},
+        {label: 'UTC+5:30(IDT)', value: 'GMT+5:30'},
+        {label: 'UTC+5:45(NPT)', value: 'GMT+5:45'},
+        {label: 'UTC+6(BHT)', value: 'GMT+6'},
+        {label: 'UTC+6:30(MRT)', value: 'GMT+6:30'},
+        {label: 'UTC+7(IST)', value: 'GMT+7'},
+        {label: 'UTC+8(EAT)', value: 'GMT+8'},
+        {label: 'UTC+8:30(KRT)', value: 'GMT+8:30'},
+        {label: 'UTC+9(FET)', value: 'GMT+9'},
+        {label: 'UTC+9:30(ACST)', value: 'GMT+9:30'},
+        {label: 'UTC+10(AEST)', value: 'GMT+10'},
+        {label: 'UTC+10:30(FAST)', value: 'GMT+10:30'},
+        {label: 'UTC+11(VTT)', value: 'GMT+11'},
+        {label: 'UTC+11:30(NFT)', value: 'GMT+11:30'},
+        {label: 'UTC+12(PSTB)', value: 'GMT+12'},
+        {label: 'UTC+12:45(CIT)', value: 'GMT+12:45'},
+        {label: 'UTC+13(PSTC)', value: 'GMT+13'},
+        {label: 'UTC+14(PSTD)', value: 'GMT+14'}
+      ]
+
+    }
+  },
+  methods: {
+
+    next () {
+      // if (this.active++ > 2) this.active = 0
+      this.active++
+      switch (this.active) {
+        case 0 :
+          // console.log('000')
+          break
+        case 1 :
+          this.bottonDisable.pre = false
+          break
+        case 2 :
+          break
+        case 3 :
+          this.goal_info.config = this.datacForm
+          this.buttonFinish = true
+          // console.info(this.$refs.buttonFinish)
+          break
+        default :
+          // console.log('000')
+      }
+    },
+    preStey () {
+      // if (this.active-- < 0) this.active = 3
+      this.active--
+      this.buttonFinish = false
+      if (this.active <= 0) {
+        this.bottonDisable.pre = true
+        this.bottonDisable.next = true
+      }
+    },
+    treeNodeClick (data, node) {
+      this.tableData=[]
+      if (data.limit === 'children') {
+        // node.data.icon = 'iconfont icon-xitong4'
+        this.chooseEqpment = data
+        console.log(this.chooseEqpment)
+        // 进行ajax 请求
+
+        // table = this.chooseEqpment.hbaseTableName
+        var info = {
+          db: 'db=default',
+          table: 'table=demo_src'
+        }
+        this.$http({
+          url: this.$http.snoopUrl('/v1/metadata/hive/table?') + info.db + '&' + info.table,
+          method: 'get'
+        }).then((data) => {
+          if (data.status === 200) {
+            var res = data.data.sd.cols
+            for (const resKey in res) {
+              var table = {
+                colname: res[resKey].name,
+                type: res[resKey].type,
+                comment: res[resKey].comment
+              }
+              this.tableData.push(table)
+            }
+          } else {
+            this.$message({
+              message: '获取   ' + this.chooseEqpment.name + '   列数据失败',
+              type: 'error'
+            })
+          }
+        })
+        // this.tableData = [{
+        //   colname: 'id',
+        //   type: 'bigint',
+        //   comment: '测试字段1'
+        // }, {
+        //   colname: 'age',
+        //   type: 'int',
+        //   comment: '测试字段2'
+        // }, {
+        //   colname: 'desc',
+        //   type: 'string',
+        //   comment: '测试字段3'
+        // }]
+      }
+    },
+    handleSelectionChange (data) {
+      if (data.length > 0) {
+        this.bottonDisable.next = false
+      } else {
+        this.bottonDisable.next = true
+      }
+      this.goal_info = {
+        equipment: this.chooseEqpment,
+        chooseCloum: data
+      }
+      // chooseCloum.cloum = data
+      var patt = new RegExp('int|double|float/i')
+      if (patt.test(this.type)) {
+        this.isNum = true
+      }
+      for (let da of data) {
+        if (patt.test(da.type)) {
+          da.isNum = true
+          da.dropdownList = this.options_int
+        } else {
+          da.isNum = false
+          da.dropdownList = this.options_str
+        }
+      }
+    },
+    showrule () {
+      this.isShowRulesDrog = true
+      this.$nextTick(() => {
+        this.$refs.ShowRulesDrogref.init()
+      })
+    },
+    // 向后台提交数据
+    async finallySubmit () {
+      this.newMeasure = {
+        'name': this.goal_info.config.name,
+        'measure.type': 'griffin',
+        'dq.type': 'PROFILING',
+        'rule.description': {
+          'details': this.rulesDesc
+        },
+        'process.type': 'BATCH',
+        'owner': this.userName,
+        'description': this.goal_info.config.description,
+        'data.sources': [
+          {
+            'name': 'source',
+            'connector': {
+              'name': 'source' + new Date().getTime(),
+              'type': 'HIVE',
+              'version': '1.2',
+              'data.unit': this.goal_info.config.num + this.goal_info.config.timetype,
+              'data.time.zone': this.goal_info.config.timezone,
+              'config': {
+                // 'database': 'default',
+                // 'table.name': this.goal_info.equipment.hbaseTableName,
+                'database': 'default',
+                'table.name': 'demo_src',
+                'where': this.goal_info.config.where
+              }
+            }
+          }
+        ],
+        'evaluate.rule': {
+          'out.dataframe.name': 'profiling',
+          rules: []
+        }
+      }
+      this.getGrouprule()
+      console.log(666)
+      console.log(this.newMeasure)
+      await this.$http({
+        url: this.$http.snoopUrl('/v1/measures'),
+        method: 'post',
+        data: this.$http.adornData(this.newMeasure)
+      }).then((datas) => {
+        console.log(datas)
+        if (datas.status === 201) {
+          return true
+        } else {
+          return false
+        }
+      })
+    },
+    submitForm () {
+      var submit = this.finallySubmit()
+      setTimeout(() => {
+        if (submit) {
+          this.$message({
+            message: '度量标准   ' + this.datacForm.name + '   创建成功',
+            type: 'success'
+          })
+        } else {
+          this.$message({
+            message: '度量标准   ' + this.datacForm.name + '   创建失败',
+            type: 'error'
+          })
+        }
+        this.$router.replace({path: '/snoop-home', query: {'status': '0'}})
+        this.$destroy()
+      }, 2 * 1000)
+    },
+    getGrouprule () {
+      var selected = {name: ''}
+      var value = ''
+      var nullvalue = ''
+      var nullname = ''
+      var enmvalue = ''
+      var regexvalue = ''
+      var regexname = ''
+      var grpname = ''
+      for (const key in this.goal_info.chooseCloum) {
+        selected.name = this.goal_info.chooseCloum[key].colname
+        let info = ''
+        let otherinfo = ''
+        for (let i = 0; i < this.goal_info.chooseCloum[key].rule.length; i++) {
+          const originrule = this.goal_info.chooseCloum[key].rule[i]
+          info = info + originrule + ','
+
+          if (originrule === 'Enum Detection Top10 Count') {
+            enmvalue = this.transferRule(originrule, selected)
+            grpname = `${selected.name}_top10count`
+            this.transenumrule.push(enmvalue)
+            this.pushEnmRule(enmvalue, grpname)
+          } else if (originrule === 'Null Count') {
+            nullvalue = this.transferRule(originrule, selected)
+            nullname = `${selected.name}_nullcount`
+            this.transnullrule.push(nullvalue)
+            this.pushNullRule(nullvalue, nullname)
+          } else if (originrule === 'Empty Count') {
+            nullvalue = this.transferRule(originrule, selected)
+            nullname = `${selected.name}_emptycount`
+            this.transnullrule.push(nullvalue)
+            this.pushNullRule(nullvalue, nullname)
+          } else if (originrule === 'Regular Expression Detection Count') {
+            selected['regex'] = ''
+            regexvalue = this.transferRule(originrule, selected)
+            regexname = `${selected.name}_regexcount`
+            this.transregexrule.push(regexvalue)
+            this.pushRegexRule(regexvalue, regexname)
+          } else {
+            otherinfo = otherinfo + originrule + ','
+            value = this.transferRule(originrule, selected)
+            this.transrule.push(value)
+          }
+        }
+
+        info = info.substring(0, info.lastIndexOf(','))
+        this.rulesDesc.push({
+          name: this.goal_info.chooseCloum[key].colname,
+          infos: info
+        })
+      }
+      if (this.transrule.length !== 0) {
+        this.getRule(this.transrule)
+      }
+    },
+    getRule (trans) {
+      var rule = ''
+      for (let i of trans) {
+        rule = rule + i + ','
+      }
+      rule = rule.substring(0, rule.lastIndexOf(','))
+      this.pushRule(rule)
+    },
+    pushEnmRule (rule, grpname) {
+      this.newMeasure['evaluate.rule'].rules.push({
+        'dsl.type': 'griffin-dsl',
+        'dq.type': 'PROFILING',
+        rule: rule,
+        'out.dataframe.name': grpname,
+        'out': [
+          {
+            'type': 'metric',
+            'name': grpname,
+            'flatten': 'array'
+          }
+        ]
+      })
+    },
+    pushNullRule (rule, nullname) {
+      this.newMeasure['evaluate.rule'].rules.push({
+        'dsl.type': 'griffin-dsl',
+        'dq.type': 'PROFILING',
+        rule: rule,
+        'out.dataframe.name': nullname
+      })
+    },
+
+    pushRegexRule (rule, nullname) {
+      this.newMeasure['evaluate.rule'].rules.push({
+        'dsl.type': 'griffin-dsl',
+        'dq.type': 'PROFILING',
+        rule: rule,
+        'out.dataframe.name': nullname
+      })
+    },
+
+    pushRule (rule) {
+      this.newMeasure['evaluate.rule'].rules.push({
+        'dsl.type': 'griffin-dsl',
+        'dq.type': 'PROFILING',
+        rule: rule,
+        name: 'profiling'
+      })
+    },
+    transferRule (rule, col) {
+      switch (rule) {
+        case 'Total Count':
+          return (
+            `count(source.${col.name}) AS \`${col.name}_count\``
+          )
+        case 'Distinct Count':
+          return (
+            `approx_count_distinct(source.${col.name}) AS \`${col.name}_distcount\``
+          )
+        case 'Null Count':
+          return (
+            `count(source.${col.name}) AS \`${col.name}_nullcount\` WHERE source.${col.name} IS NULL`
+          )
+        case 'Maximum':
+          return (
+            `max(source.${col.name}) AS \`${col.name}_max\``
+          )
+        case 'Minimum':
+          return (
+            `min(source.${col.name}) AS \`${col.name}_min\``
+          )
+        case 'Average':
+          return (
+            `avg(source.${col.name}) AS \`${col.name}_average\``
+          )
+        case 'Empty Count':
+          return (
+            `count(source.${col.name}) AS \`${col.name}_emptycount\` WHERE source.${col.name} = ''`
+          )
+        case 'Regular Expression Detection Count':
+          return (
+            `count(source.${col.name}) AS \`${col.name}_regexcount\` WHERE source.${col.name} RLIKE '^[0-9]{4}$'`
+          )
+        case 'Enum Detection Top10 Count':
+          return (
+            `source.${col.name} AS ${col.name}, count(*) AS count GROUP BY source.${col.name} ORDER BY count DESC LIMIT 10`
+          )
+      }
+    },
+    getCoal (resolve) {
+      let coalAll = []
+      this.$http({
+        url: this.$http.adornUrl('/dataset/tree/getcoal'),
+        method: 'post'
+      }).then(({data}) => {
+        if (!(data && data.code === 0)) {
+          this.$message.error(data.msg)
+        } else {
+          const coalContent = data.info
+          for (let sign = 0; sign < coalContent.length; sign++) {
+            var coal = {
+              name: coalContent[sign].name,
+              coalid: coalContent[sign].id,
+              limit: 'coal',
+              hbaseTableName: '',
+              icon: 'iconfont icon-xitongfuwu',
+              leaf: false
+            }
+            coalAll.push(coal)
+          }
+        }
+        return resolve(coalAll)
+        // console.log('***************')
+        // console.log(coalAll)
+      })
+    },
+    getSystem (coalId, resolve) {
+      var coalData = {
+        coalid: coalId
+      }
+      this.$http({
+        url: this.$http.adornUrl('/dataset/tree/getfisystem'),
+        method: 'post',
+        data: this.$http.adornData(coalData, false)
+      }).then(systemRespond => {
+        let systemAll = []
+        if (systemRespond.data.length <= 0) {
+          this.$message.error(systemRespond.msg)
+        } else {
+          const systemContent = systemRespond.data
+          for (let i = 0; i < systemContent.length; i++) {
+            var system = {
+              name: systemContent[i].name,
+              systemid: systemContent[i].id,
+              limit: 'system',
+              icon: 'icon iconfont icon-xitongfuwu',
+              hbaseTableName: '',
+              leaf: false
+            }
+            systemAll.push(system)
+          }
+        }
+        return resolve(systemAll)
+      })
+    },
+    getSubSystem (systemid, resolve) {
+      const systemData = {
+        systemid: systemid
+      }
+      this.$http({
+        url: this.$http.adornUrl('/dataset/tree/getsubSystem'),
+        method: 'post',
+        data: this.$http.adornData(systemData, false)
+      }).then(subrespond => {
+        let subSystemAll = []
+        let subSystemContent = subrespond.data
+        if (subSystemContent.length === 0) {
+          this.$message.error(subrespond.msg)
+        } else {
+          for (let j = 0; j < subSystemContent.length; j++) {
+            var subSystem = {
+              name: subSystemContent[j].name,
+              subSustemid: subSystemContent[j].id,
+              limit: 'subSystem',
+              icon: 'iconfont icon-xitongfuwu',
+              leaf: false
+            }
+            subSystemAll.push(subSystem)
+          }
+        }
+        return resolve(subSystemAll)
+      })
+    },
+    getEquipment (subSustemid, resolve) {
+      const subSystemData = {
+        subSystemid: subSustemid
+      }
+      this.$http({
+        url: this.$http.adornUrl('/dataset/tree/getequipment'),
+        method: 'post',
+        data: this.$http.adornData(subSystemData, false)
+      }).then(eqprespond => {
+        let equipmentAll = []
+        const eqpSystemContent = eqprespond.data
+        if (eqpSystemContent.length === 0) {
+          this.$message.error(eqprespond.msg)
+        } else {
+          for (let k = 0; k < eqpSystemContent.length; k++) {
+            var equipment = {
+              name: eqpSystemContent[k].name,
+              equipmentid: eqpSystemContent[k].id,
+              limit: 'equipment',
+              hbaseTableName: '',
+              icon: 'iconfont icon-xitongfuwu',
+              leaf: false
+            }
+            equipmentAll.push(equipment)
+          }
+        }
+        return resolve(equipmentAll)
+      })
+    },
+    getSubEquipment (equipmentid, resolve) {
+      const equimentData = {
+        equipment: equipmentid
+      }
+      this.$http({
+        url: this.$http.adornUrl('/dataset/tree/getsubequipment'),
+        method: 'post',
+        data: this.$http.adornData(equimentData, false)
+      }).then(subEqpRespond => {
+        let subEquimentAll = []
+        const subEqpSystemContent = subEqpRespond.data
+        if (subEqpSystemContent.length === 0) {
+          this.$message.error(subEqpRespond.msg)
+        } else {
+          for (let m = 0; m < subEqpSystemContent.length; m++) {
+            var subEquiment = {
+              name: subEqpSystemContent[m].name,
+              subEquipmentid: subEqpSystemContent[m].id,
+              hbaseTableName: subEqpSystemContent[m].hbaseTableName,
+              limit: 'children',
+              icon: 'iconfont icon-xitong3',
+              leaf: true
+            }
+            subEquimentAll.push(subEquiment)
+          }
+        }
+        return resolve(subEquimentAll)
+      })
+    },
+    // 左侧树的懒加载
+    loadTreeNode (node, resolve) {
+      if (node.level === 0) {
+        this.getCoal(resolve)
+      } else if (node.level === 1) {
+        this.getSystem(node.data.coalid, resolve)
+      } else if (node.level === 2) {
+        this.getSubSystem(node.data.systemid, resolve)
+      } else if (node.level === 3) {
+        this.getEquipment(node.data.subSustemid, resolve)
+      } else {
+        this.getSubEquipment(node.data.equipmentid, resolve)
+      }
+    }
+  },
+  watch: {
+    chooseEqpment (newVal, oldVal) {
+      if (oldVal.hasOwnProperty('icon')) {
+        oldVal.icon = 'iconfont icon-xitong3'
+      }
+      newVal.icon = 'iconfont icon-xitong4'
+    }
+  }
+}
+</script>
+<style scoped>
+.divi {
+  display: block;
+  height: 1px;
+  width: 100%;
+  margin: 24px 0;
+  background-color: #dcdfe6;
+  position: relative;
+}
+
+.divi2 {
+  display: block;
+  height: 1px;
+  width: 100%;
+  position: relative;
+}
+
+.showRules {
+  color: #409eff
+}
+</style>

+ 97 - 0
src/views/modules/snoop/measureUserWatch.vue

@@ -0,0 +1,97 @@
+<template>
+<div>
+
+
+
+  <el-dialog :title="'查看度量'" :close-on-click-modal="false" :visible.sync="visible">
+
+    <el-form  :model="goal_info" label-width="100px"  v-loading="measDataListLoading"  style="word-break: break-word">
+      <el-form-item label="度量名称 :" prop="name">
+        <!--                  <el-input v-model="goal_info.config.name"></el-input>-->
+        <span>{{ goal_info.name  }}</span>
+      </el-form-item>
+      <el-form-item label="度量描述 :">
+        <span>{{ goal_info.description }}</span>
+        <!--                  <el-input v-model="goal_info.config.description"></el-input>-->
+      </el-form-item>
+      <el-form-item label="数据源 :">
+        <span>{{ goal_info.dataSources }}</span>
+        <!--                  <el-input v-model="goal_info.equipment.name"></el-input>-->
+      </el-form-item>
+      <!--                <el-form-item label="查询条件 :">-->
+      <!--                  <span>{{goal_info.config.where}}</span>-->
+      <!--&lt;!&ndash;                  <el-input v-model="goal_info.config.where"></el-input>&ndash;&gt;-->
+      <!--                </el-form-item>-->
+      <el-form-item label="分区信息 :">
+        <span>{{goal_info.partition}}</span>
+        <!--                                  <el-input v-model="goal_info.config.num" >{{goal_info.config.num + goal_info.config.timetype}}</el-input>-->
+        <!--                                  <el-input v-model="goal_info.config.timezone"></el-input>-->
+      </el-form-item>
+      <el-form-item label="度量规则 :">
+        <!--                  <el-input-number v-model="datacForm.num" controls-position="right" :min="0"></el-input-number>-->
+<!--        <div style="height: 100px;overflow: auto">-->
+          <template v-for="rule in  goal_info.Cloum">
+            <li>{{ rule.name }} : {{ rule.infos }}</li>
+          </template>
+
+<!--          &lt;!&ndash;    <template v-for="rule in  timezones" >&ndash;&gt;-->
+<!--          &lt;!&ndash;      <li>{{rule.label}} : {{rule.value}}</li>&ndash;&gt;-->
+<!--          &lt;!&ndash;    </template>&ndash;&gt;-->
+<!--        </div>-->
+      </el-form-item>
+    </el-form>
+
+
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="visible = false">取消</el-button>
+      <el-button type="primary" @click="visible=false">确定</el-button>
+    </span>
+  </el-dialog>
+
+</div>
+</template>
+
+<script>
+export default {
+  name: 'measureUserWatch',
+  data () {
+    return {
+      goal_info: {},
+      visible: false,
+      measDataListLoading: false
+    }
+  },
+  methods: {
+    init (info) {
+      this.visible = true
+      this.measDataListLoading = false
+      // console.info(info)
+      this.getMeasureListById(info.measureID)
+    },
+    getMeasureListById (id) {
+      this.measDataListLoading = true
+      this.classificationtag = null
+      this.$http({
+        url: this.$http.snoopUrl('/v1/measures/') + id,
+        methods: 'get'
+      }).then((data) => {
+        if (data && data.status === 200) {
+          var info = data.data
+          this.goal_info.name = info.name
+          this.goal_info.description = info.description
+          var dataScore = info['data.sources'][0].connector.config
+          this.goal_info.dataSources = dataScore.database + '  : ' + dataScore['table.name']
+          this.goal_info.partition = info['data.sources'][0].connector['data.unit']
+          this.goal_info.Cloum = info['rule.description'].details
+        }
+        // console.log(this.goal_info)
+        this.measDataListLoading = false
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 593 - 0
src/views/modules/snoop/showRules.vue

@@ -0,0 +1,593 @@
+<template>
+  <div>
+    <el-dialog
+      :title="title"
+      :visible.sync="showRuleVisible"
+      width="60%"
+      append-to-body
+      close-on-click-modal
+      close-on-press-escape
+      center>
+      <div>
+        <el-tabs tab-position="left" style="height: 420px; overflow: auto;">
+
+          <el-tab-pane label="Null Count">
+            <div class="table-style1">
+              <p>Null is a special marker used to indicate that a data value does not exist.</p>
+              <h5 class="table-style2">Example</h5>
+              <p style="text-align: left;">
+                Suppose we have the below data table, then the Null Count of "FIRSTNAME" is
+                <span class="table-style3">2</span>
+              </p>
+              <el-table
+                :data="tableDataNull"
+                style="width: 100%">
+                <el-table-column
+                  prop="id"
+                  label="ID"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="FRISTNAME"
+                  label="FRISTNAME"
+                  width="180">
+                  <template slot-scope="scope">
+                    <span v-if="scope.row.FRISTNAME === 'null'" style="color: red">{{ scope.row.FRISTNAME }}</span>
+                    <span v-else>{{ scope.row.FRISTNAME }}</span>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  prop="LASTNAME"
+                  label="LASTNAME">
+                </el-table-column>
+                <el-table-column
+                  prop="age"
+                  label="AGE">
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+
+          <el-tab-pane label="Distinct Count">
+            <div class="table-style1">
+              <p>The Distinct Constraint prevents two records from having identical values in a particular column.</p>
+              <h5 class="table-style2">Example</h5>
+              <p style="text-align: left;">
+                Suppose we have the below data table which has four attributes-ID, NAME, AGE and FAVOURITE FOOD. Then
+                the Distinct Count is
+                <span class="table-style3">2</span>
+              </p>
+              <el-table
+                :data="tableDataDistinct"
+                style="width: 100%">
+                <el-table-column
+                  prop="id"
+                  label="ID">
+                </el-table-column>
+                <el-table-column
+                  prop="name"
+                  label="NAME"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="age"
+                  label="AGE">
+                </el-table-column>
+                <el-table-column
+                  prop="food"
+                  label="FAVOURITE FOOD">
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+
+          <el-tab-pane label="Total Count">
+            <div class="table-style1">
+              <p>Total Count is a count of the number of values of the selected column.</p>
+              <h5 class="table-style2">Example</h5>
+              <p style="text-align: left;">
+                Suppose we have the below data table, then the total count of ID is:
+                <span class="table-style3">5</span>
+              </p>
+              <el-table
+                :data="tableDataNull"
+                style="width: 100%">
+                <el-table-column
+                  prop="id"
+                  label="ID"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="FRISTNAME"
+                  label="FRISTNAME"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="LASTNAME"
+                  label="LASTNAME">
+                </el-table-column>
+                <el-table-column
+                  prop="age"
+                  label="AGE">
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+
+          <el-tab-pane label="Maximum">
+            <div class="table-style1">
+              <p>Maximum is the biggest value of the selected column.</p>
+              <h5 class="table-style2">Example</h5>
+              <p style="text-align: left;">
+                Suppose we have the below data table, then the maximum is:
+                <span class="table-style3">24</span>
+              </p>
+              <el-table
+                :data="tableDataNull"
+                style="width: 100%">
+                <el-table-column
+                  prop="id"
+                  label="ID"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="FRISTNAME"
+                  label="FRISTNAME"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="LASTNAME"
+                  label="LASTNAME">
+                </el-table-column>
+                <el-table-column
+                  prop="age"
+                  label="AGE">
+                  <template slot-scope="scope">
+                    <span v-if="scope.row.LASTNAME === 'aaa'" style="color: #ff0000">{{ scope.row.age }}</span>
+                    <span v-else>{{ scope.row.age }}</span>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+
+          <el-tab-pane label="Minimum">
+            <div class="table-style1">
+              <p>Minimum is smallest value of the selected colum.</p>
+              <h5 class="table-style2">Example</h5>
+              <p style="text-align: left;">
+                Suppose we have the below data table, then the minimum is:
+                <span class="table-style3">20</span>
+              </p>
+              <el-table
+                :data="tableDataNull"
+                style="width: 100%">
+                <el-table-column
+                  prop="id"
+                  label="ID"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="FRISTNAME"
+                  label="FRISTNAME"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="LASTNAME"
+                  label="LASTNAME">
+                </el-table-column>
+                <el-table-column
+                  prop="age"
+                  label="AGE">
+                  <template slot-scope="scope">
+                    <span v-if="scope.row.LASTNAME === 'zhang'" style="color: #ff0000">{{ scope.row.age }}</span>
+                    <span v-else>{{ scope.row.age }}</span>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+
+          <el-tab-pane label="Average">
+            <div class="table-style1">
+              <p>Average is the average of the data values.</p>
+              <h5 class="table-style2">Example</h5>
+              <p style="text-align: left;">
+                Suppose we have the below data table, then the Average of AGE is:
+                <span class="table-style3">22</span>
+              </p>
+              <el-table
+                :data="tableDataNull"
+                style="width: 100%">
+                <el-table-column
+                  prop="id"
+                  label="ID"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="FRISTNAME"
+                  label="FRISTNAME"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="LASTNAME"
+                  label="LASTNAME">
+                </el-table-column>
+                <el-table-column
+                  prop="age"
+                  label="AGE">
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+
+          <el-tab-pane label="Median">
+            <div class="table-style1">
+              <p>Median is the middle value.</p>
+              <h5 class="table-style2">Example</h5>
+              <p style="text-align: left;">
+                Suppose we have the below data table, then the median of AGE is:
+                <span class="table-style3">22</span>
+              </p>
+              <el-table
+                :data="tableDataNull"
+                style="width: 100%">
+                <el-table-column
+                  prop="id"
+                  label="ID"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="FRISTNAME"
+                  label="FRISTNAME"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="LASTNAME"
+                  label="LASTNAME">
+                </el-table-column>
+                <el-table-column
+                  prop="age"
+                  label="AGE">
+                  <template slot-scope="scope">
+                    <span v-if="scope.row.LASTNAME === 'wang'" style="color: #ff0000">{{ scope.row.age }}</span>
+                    <span v-else>{{ scope.row.age }}</span>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+
+          <el-tab-pane label="TOP 10">
+            <div class="table-style1">
+              <p> The top ten most frequent occurrences in the data.</p>
+              <h5 class="table-style2">Example</h5>
+              <p style="text-align: left;">
+                Suppose we have the below data table, then cloum LASTNAME of  top 18 is:
+                <span class="table-style3" style="font-size: 15px">(cao,3);(fan,2);(ding,2); (huang,2); (hua,1) (han,1) (li,1) (liu,1); (lv,1); (guan,1)</span>
+              </p>
+              <el-table
+                :data="tableDataTop10"
+                style="width: 100%">
+                <el-table-column
+                  prop="id"
+                  label="ID"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="FRISTNAME"
+                  label="FRISTNAME"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="LASTNAME"
+                  label="LASTNAME">
+                </el-table-column>
+                <el-table-column
+                  prop="age"
+                  label="AGE">
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-tab-pane>
+          <!--          <el-tab-pane label="Rule Detection Count">定时任务补偿</el-tab-pane>-->
+          <el-tab-pane label="Enum Detection Count">
+            <div class="table-style1">
+              <p>The Enum Detection Count is a statement of GROUP BY , which often used with aggregate functions
+                (COUNT, MAX, MIN, SUM, AVG) to group the result-set by one or more columns.</p>
+              <h5 class="table-style2">Example</h5>
+              <p style="text-align: left;">
+                We can use Enum Detection Count to search the lists the number of customers in each country in the
+                following table:
+              </p>
+              <el-table
+                :data="tableDataSQL"
+                style="width: 100%">
+                <el-table-column
+                  prop="id"
+                  label="ID"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="name"
+                  label="NAME"
+                  width="180">
+                </el-table-column>
+                <el-table-column
+                  prop="age"
+                  label="AGE">
+                </el-table-column>
+                <el-table-column
+                  prop="country"
+                  label="COUNTRY">
+                </el-table-column>
+              </el-table>
+              <p class="text-left">
+                <strong>Suppose Enum Expression is :</strong> SELECT COUNT(CUSTORMERID), COUNTRY FROM Customers GROUP
+                BY COUNTRY;
+              </p>
+              <p class="text-left">
+                The result is:
+                <br>
+                <template style="margin-left: 10px">
+                  <kbd>COUNTRY |COUNT(CUSTOMERID)
+                    <br> China |2
+                    <br> Mexico |2
+                    <br> UK |1
+                  </kbd>
+                </template>
+              </p>
+            </div>
+          </el-tab-pane>
+          <!--          <el-tab-pane label="Regular Expression Match">定时任务补偿</el-tab-pane>-->
+
+        </el-tabs>
+
+      </div>
+
+      <span slot="footer" class="dialog-footer">
+    <el-button @click="showRuleVisible = false">取 消</el-button>
+    <el-button type="primary" @click="showRuleVisible = false">确 定</el-button>
+  </span>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'showRules',
+  data () {
+    return {
+      showRuleVisible: false,
+      vaType: null,
+      title: 'rules',
+      tableDataNull: [
+        {
+          id: 1,
+          FRISTNAME: 'san',
+          LASTNAME: 'zhang',
+          age: 20
+        }, {
+          id: 2,
+          FRISTNAME: 'si',
+          LASTNAME: 'li',
+          age: 21
+        }, {
+          id: 3,
+          FRISTNAME: 'null',
+          LASTNAME: 'wang',
+          age: 22
+        }, {
+          id: 4,
+          FRISTNAME: 'liu',
+          LASTNAME: 'tang',
+          age: 23
+        }, {
+          id: 5,
+          FRISTNAME: 'null',
+          LASTNAME: 'aaa',
+          age: 24
+        }
+      ],
+      tableDataTop10: [
+        {
+          id: 1,
+          FRISTNAME: 'cao',
+          LASTNAME: 'cao',
+          age: 60
+        }, {
+          id: 2,
+          FRISTNAME: 'zhi',
+          LASTNAME: 'cao',
+          age: 40
+        }, {
+          id: 3,
+          FRISTNAME: 'hong',
+          LASTNAME: 'cao',
+          age: 20
+        }, {
+          id: 4,
+          FRISTNAME: 'lao',
+          LASTNAME: 'fan',
+          age: 20
+        }, {
+          id: 5,
+          FRISTNAME: 'kang',
+          LASTNAME: 'fan',
+          age: 20
+        },
+        {
+          id: 6,
+          FRISTNAME: 'feng',
+          LASTNAME: 'ding',
+          age: 20
+        }, {
+          id: 7,
+          FRISTNAME: 'yi',
+          LASTNAME: 'ding',
+          age: 20
+        }, {
+          id: 8,
+          FRISTNAME: 'zhong',
+          LASTNAME: 'huang',
+          age: 20
+        }, {
+          id: 9,
+          FRISTNAME: 'gai',
+          LASTNAME: 'huang',
+          age: 20
+        }, {
+          id: 10,
+          FRISTNAME: 'gai',
+          LASTNAME: 'huang',
+          age: 20
+        }, {
+          id: 11,
+          FRISTNAME: 'tuo',
+          LASTNAME: 'hua',
+          age: 20
+        }, {
+          id: 12,
+          FRISTNAME: 'xin',
+          LASTNAME: 'han',
+          age: 20
+        },
+        {
+          id: 13,
+          FRISTNAME: 'tong',
+          LASTNAME: 'li',
+          age: 20
+        },
+        {
+          id: 14,
+          FRISTNAME: 'bei',
+          LASTNAME: 'liu',
+          age: 20
+        },
+        {
+          id: 15,
+          FRISTNAME: 'bu',
+          LASTNAME: 'lv',
+          age: 20
+        }, {
+          id: 16,
+          FRISTNAME: 'yu',
+          LASTNAME: 'guan',
+          age: 20
+        }
+      ],
+      tableDataDistinct: [{
+        id: 1,
+        name: 'Lily',
+        age: 20,
+        food: 'APPLE'
+      }, {
+        id: 2,
+        name: 'Lucy',
+        age: 20,
+        food: 'APPLE'
+      }, {
+        id: 3,
+        name: 'Sam',
+        age: 35,
+        food: 'banana'
+      }, {
+        id: 4,
+        name: 'David',
+        age: 35,
+        food: 'banana'
+      }],
+      tableDataSQL: [{
+        id: 1,
+        name: 'allen',
+        age: 20,
+        country: 'China'
+      }, {
+        id: 2,
+        name: 'allen',
+        age: 20,
+        country: 'China'
+      }, {
+        id: 3,
+        name: 'allen',
+        age: 20,
+        country: 'UK'
+      }, {
+        id: 4,
+        name: 'allen',
+        age: 20,
+        country: 'Mexico'
+      }, {
+        id: 5,
+        name: 'allen',
+        age: 20,
+        country: 'Mexico'
+      }]
+    }
+  },
+  methods: {
+    init () {
+      this.showRuleVisible = true
+    },
+    ngOnInit () {
+      this.vaType = '2'
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+.table > thead > tr.success > td {
+  background-color: #77b300;
+}
+
+mark {
+  background-color: #ff8800;
+  padding: .2em;
+}
+
+h5 {
+  font-size: 20px;
+}
+
+.table-style1 {
+  margin-left: 10px;
+  margin-top: -10px;
+  word-break: break-word
+}
+
+.table-style2 {
+  text-align: left;
+  margin: 5px 0px 10px 5px;
+}
+
+.table-style3 {
+  font-size: 20px;
+  margin-left: 10px;
+  color: #2f4f46;
+}
+
+.text-left {
+  text-align: left;
+}
+
+.y-scrollable::-webkit-scrollbar-track {
+  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+  border-radius: 10px;
+  background-color: #F5F5F5;
+}
+
+.y-scrollable::-webkit-scrollbar {
+  width: 5px;
+  border-radius: 10px;
+  background-color: #F5F5F5;
+}
+
+.y-scrollable::-webkit-scrollbar-thumb {
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
+  background-color: #AAAAAA;
+}
+</style>

+ 47 - 0
src/views/modules/snoop/snoop-add.vue

@@ -0,0 +1,47 @@
+<template>
+  <el-dialog :title="'新增度量'" :close-on-click-modal="false" :visible.sync="visible">
+    <el-form :model="dataForm" ref ='dataForm'
+      label-width="100px">
+      <el-form-item label="步骤" size="mini">
+        <el-radio-group v-model="datasetStatus">
+          <el-radio :label="1">1.创建度量标准</el-radio>
+          <el-radio :label="2">2.创建任务</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="visible = false">取消</el-button>
+      <el-button type="primary" @click="visible=false;creatDataSetHandle()">确定</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  data () {
+    return {
+      visible: false,
+      datasetStatus: 1,
+      dataForm: {}
+    }
+  },
+  methods: {
+    init () {
+      this.visible = true
+      this.$nextTick(() => {
+        this.$refs.dataForm.resetFields()
+      })
+    },
+    // 创建measure
+    creatDataSetHandle () {
+      // 如果为传统算法
+      if (this.datasetStatus === 1) {
+        this.$router.replace({ path: '/snoop-measure' })
+      } else {
+        // 如果为智能算法
+        this.$router.replace({ path: '/snoop-job' })
+      }
+    }
+  }
+}
+</script>