haoxuan
2023-09-01 1e1e5f612f252d66b0d0386cf52873bb1f3f7d7b
系统维护+系统监控器+调试工具的app 增加
9个文件已添加
3个文件已修改
2234 ■■■■■ 已修改文件
public/apps.json 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/images/app-mid/debuggTool.png 补丁 | 查看 | 原始文档 | blame | 历史
public/images/app-mid/productionSign.png 补丁 | 查看 | 原始文档 | blame | 历史
public/images/desktop/header-icon/SmartAI-新.png 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/debuggTool/index/App.vue 617 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/debuggTool/index/main.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/desktop/index/components/ToolsEntry.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemMonitor/api/api.ts 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemMonitor/index/App.vue 853 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/systemMonitor/index/main.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/vindicate/index/App.vue 617 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/vindicate/index/main.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/apps.json
@@ -93,17 +93,85 @@
      "isUpgrade": false,
      "progressMsg": ""
    },
    {
      "id": "ee64fe23-7631-4ef9-9aca-ea09673693be",
      "name": "系统维护",
      "package": "vindicate",
      "type": "2",
      "url": "/view/vindicate/",
      "title": "系统维护",
      "width": 760,
      "height": 534,
      "iconBlob": "",
      "icon": "../../images/app-mid/vindicate.png",
      "version": "1.0.0",
      "create_time": "2020-10-09 14:00:11",
      "create_by": "",
      "update_time": "",
      "update_by": "",
      "isDelete": 0,
      "isDefault": true,
      "remoteVersion": "",
      "installed": true,
      "isUpgrade": false,
      "progressMsg": ""
    },
    {
      "id": "fa5674ee-70cf-4e22-8a06-c17429fb777",
      "name": "生产管理看板",
      "name": "生产任务",
      "package": "shuohuangMonitorAnalyze",
      "type": "1",
      "url": "http://192.168.20.125:8080",
      "title": "生产管理看板",
      "title": "生产任务",
      "width": 0,
      "height": 0,
      "iconBlob": "",
      "icon": "../../images/app-mid/productionSign.png",
      "version": "1.0.0",
      "create_time": "2020-10-09 14:00:08",
      "create_by": "",
      "update_time": "",
      "update_by": "",
      "isDelete": 0,
      "isDefault": false,
      "remoteVersion": "",
      "installed": true,
      "isUpgrade": false,
      "progressMsg": ""
    },
    {
      "id": "fa5674ee-70cf-4e22-8a06-c17429fb777",
      "name": "调试工具",
      "package": "debuggTool",
      "type": "2",
      "url": "/view/debuggTool/",
      "title": "调试工具",
      "width": 1500,
      "height": 750,
      "iconBlob": "",
      "icon": "../../images/app-mid/debuggTool.png",
      "version": "1.0.0",
      "create_time": "2020-10-09 14:00:08",
      "create_by": "",
      "update_time": "",
      "update_by": "",
      "isDelete": 0,
      "isDefault": false,
      "remoteVersion": "",
      "installed": true,
      "isUpgrade": false,
      "progressMsg": ""
    },
    {
      "id": "fa5674ee-70cf-4e22-8a06-c171292b777",
      "name": "系统监控",
      "package": "systemMonitor",
      "type": "2",
      "url": "/view/systemMonitor/",
      "title": "系统监控",
      "width": 1220,
      "height": 800,
      "iconBlob": "",
      "icon": "../../images/app-mid/visual.png",
      "version": "1.0.0",
      "create_time": "2020-10-09 14:00:08",
public/images/app-mid/debuggTool.png
public/images/app-mid/productionSign.png
public/images/desktop/header-icon/SmartAI-ÐÂ.png

src/pages/debuggTool/index/App.vue
New file
@@ -0,0 +1,617 @@
<template>
  <div class="s-system-manage">
    <el-tabs
      id="systemMaintenance"
      v-model="activeName"
      v-loading="loading"
      :element-loading-text="loadingText"
    >
      <el-tab-pane label="设备维护" name="device" v-if="isShow('vindicate:device')">
        <div class="s-system-maintenance">
          <div class="box-card">
            <div class="ui-top-view">
              <div class="ui-top-title">重启</div>
            </div>
            <el-divider></el-divider>
            <div class="box-card-content">
              <el-button type="primary" size="small" style="width:80px" @click="reboot">重启</el-button>
              <b class="card-text">重启节点</b>
              <el-row style="margin-top:20px">
                <el-col>
                  <vue-cron :expression="rebootCron" @update="setRebootCron" />
                </el-col>
              </el-row>
            </div>
          </div>
          <!--
          <div class="box-card">
            <div class="ui-top-view">
              <div class="ui-top-title">恢复默认值</div>
            </div>
            <el-divider></el-divider>
            <div class="box-card-content">
              <el-row>
                <el-col :span="1">
                  <el-button type="primary" size="small">简单恢复</el-button>
                </el-col>
                <el-col :span="23">
                  <b class="card-text">简单恢复设备参数</b>
                </el-col>
              </el-row>
              <el-row style="margin-top:20px">
                <el-col :span="1">
                  <el-button type="primary" size="small">完全恢复</el-button>
                </el-col>
                <el-col :span="23">
                  <b class="card-text">完全恢复设备参数到出厂设置</b>
                </el-col>
              </el-row>
            </div>
          </div>
          <div class="box-card">
            <div class="ui-top-view">
              <div class="ui-top-title">参数导入导出</div>
            </div>
            <el-divider></el-divider>
            <div class="box-card-content">
              <el-row :gutter="4">
                <el-col :span="1">
                  <el-button type="info" size="small" style="width:80px">导入</el-button>
                </el-col>
                <el-col :span="3" style="padding-left:30px">
                  <el-input placeholder="上传参数文件" size="small" :readonly="true">
                    <el-upload slot="suffix" action="https://jsonplaceholder.typicode.com/posts/">
                      <el-button
                        type="text"
                        icon="el-icon-upload2"
                        size="small"
                        style="font-size:18px; color:#0088ff"
                      ></el-button>
                    </el-upload>
                  </el-input>
                </el-col>
              </el-row>
              <el-row style="margin-top:20px">
                <el-col :span="1">
                  <el-button type="primary" size="small">设备参数</el-button>
                </el-col>
                <el-col :span="23">
                  <b class="card-text">参数导出</b>
                </el-col>
              </el-row>
            </div>
          </div>
          -->
          <div class="box-card">
            <div class="ui-top-view">
              <div class="ui-top-title">升级</div>
            </div>
          </div>
          <el-divider></el-divider>
          <div class="box-card-content">
            <el-row :gutter="4">
              <el-col :span="12">
                <file-uploader
                  single
                  uploadPlaceholder="上传升级文件"
                  url="/data/api-v/sysset/patchUpdate"
                  @complete="onFileUpload"
                  @file-added="onFileAdded"
                />
              </el-col>
              <el-col :span="2">
                <el-button
                  type="primary"
                  size="small"
                  style="width:80px"
                  @click="upgrade"
                  :disabled="!fileAdded"
                  :loading="upgrading"
                >升级</el-button>
              </el-col>
              <el-col :span="16" class="upload-msg">
                <span v-html="patchUpdateStatus"></span>
              </el-col>
            </el-row>
          </div>
        </div>
      </el-tab-pane>
      <el-tab-pane label="数据库维护" name="dbvdc" v-if="isShow('vindicate:db')">
        <div class="box">
          <p class="title">
            <label>数据清理</label>
          </p>
          <div class="range">
            <div class="left">
              <p>选择数据范围:</p>
            </div>
            <div class="middle">
              <el-date-picker
                v-model="dataRange"
                type="daterange"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                style="height:38px"
                :picker-options="pickerOptions"
              ></el-date-picker>
            </div>
            <div class="right">
              <el-button @click="deleteData" style="height:38px;background:#ff0000;color:white">删除数据</el-button>
            </div>
          </div>
          <div class="tip">
            <i class="iconfont icontishi-zhuyi"></i>
            <p class="zhuyi">请注意,按以上日期范围删除的数据不可恢复,立即生效,请谨慎操作</p>
          </div>
        </div>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script>
import { rebootServer, getDevInfo, getRebootTask, setRebootTask, fileUpload, doUpgrade, deleteDate } from "@/api/system"
import VueCron from "@/components/subComponents/VueCron"
import FileUploader from "@/components/subComponents/FileUpload/index"
export default {
  components: {
    VueCron,
    FileUploader
  },
  data() {
    return {
      timer: null,
      buttonAuthority: sessionStorage.getItem("buttonAuthoritys") || [],
      rebootCron: "",
      activeName: "device",
      restartValue: "不重启",
      restartTimeValue: new Date(2019, 9, 10, 18, 40),
      loading: false,
      loadingText: '',
      probeSum: 0,
      patchUpdateStatus: "",
      dataRange: [
        this.$moment().format("YYYY-MM-DD HH:mm:ss"),
        this.$moment().format("YYYY-MM-DD HH:mm:ss")
      ],
      fileUploadUrl: fileUpload,
      patchFile: {},
      pickerOptions: {
        disabledDate(time) {
          var day = new Date()
          day.setTime(day.getTime() - 24 * 60 * 60 * 1000)
          return time.getTime() > day;
        },
      },
      upgrading: false,
      fileAdded: false,
    };
  },
  mounted() {
    this.getRebootCron()
    if (!this.isShow('vindicate:device')) {
      this.activeName = "dbvdc"
    }
  },
  computed: {
    isAdmin() {
      if (
        sessionStorage.getItem('userInfo') &&
        sessionStorage.getItem('userInfo') !== ''
      ) {
        let loginName = JSON.parse(sessionStorage.getItem('userInfo')).username
        return (
          loginName === 'superadmin' || loginName === 'basic'
        )
      }
      return false;
    }
  },
  methods: {
    isShow(authority) {
      if (this.isAdmin) {
        return true
      } else if (
        this.buttonAuthority.indexOf(',' + authority + ',') > -1
      ) {
        return true
      } else {
        return false
      }
    },
    format(array) {
      return [
        this.$moment(array[0]).format("YYYY-MM-DD"),
        this.$moment(array[1]).format("YYYY-MM-DD")
      ];
    },
    getRebootCron() {
      getRebootTask().then(rsp => {
        this.rebootCron = rsp.data
      })
    },
    setRebootCron(value) {
      this.rebootCron = value
      setRebootTask({ task: value }).then(rsp => {
        if (rsp && rsp.success) {
          this.$notify({
            type: "success",
            message: "配置成功"
          })
        }
      }).catch(err => {
        this.$notify({
          type: "error",
          message: "配置失败"
        })
      })
    },
    reboot() {
      this.$confirm('确定要重启该节点吗?', {
        center: true,
        cancelButtonClass: 'comfirm-class-cancle',
        confirmButtonClass: 'comfirm-class-sure'
      }).then(() => {
        this.loading = true;
        this.loadingText = "智能计算节点正在重启,请耐心等待..."
        rebootServer().then(rsp => {
          this.probeServer(this.reLogin)
        }).catch(err => {
          if (err.status == 400) {
            this.loading = false;
            this.$notify({
              type: "error",
              message: "重启计算节点失败"
            })
          } else {
            this.probeServer(this.reLogin)
          }
        })
      })
    },
    deleteData() {
      var timeRange = this.format(this.dataRange);
      var showStartTime = timeRange[0]
      var showEndTime = timeRange[1]
      console.log("时间:", showStartTime, showEndTime)
      this.$confirm("提示:" + showStartTime + " è‡³ " + showEndTime + " äº§ç”Ÿçš„全部数据将被删除,此操作立即生效,不可恢复,是否删除?", {
        center: true,
        cancelButtonClass: "comfirm-class-cancle",
        confirmButtonClass: "comfirm-class-sure"
      }).then(() => {
        this.loading = true
        this.loadingText = "正在删除数据,请稍候!"
        var param = {
          startTime: showStartTime,
          endTime: showEndTime
        }
        deleteDate(param).then(resp => {
          if (resp.success) {
            this.$message({
              type: "success",
              message: "删除数据成功"
            })
            this.loading = false
          }
        }).catch(err => {
          this.$message({
            type: "error",
            message: "删除数据失败!"
          })
          this.loading = false
        })
      }).catch(() => {
        console.log("取消了!")
      })
    },
    reLogin() {
      this.$router.push("/")
    },
    probeServer(callback) {
      this.probeSum++;
      let _this = this
      if (this.probeSum > 60) {
        this.$confirm('连接服务器失败, è¯·åˆ·æ–°é¡µé¢æˆ–联系管理员', '失败', {
          type: 'error',
          cancelButtonClass: 'comfirm-class-cancle',
          confirmButtonClass: 'comfirm-class-sure'
        }).then(() => {
          // _this.$router.push("/")
          callback()
        })
        return
      }
      this.timer = setTimeout(() => {
        getDevInfo().then(() => {
          // _this.$router.push("/")
          callback()
        }).catch(err => {
          _this.probeServer(callback)
        })
      }, 10000)
    },
    onFileUpload(file) {
      this.patchUpdateStatus = `<span style="color:green">上传成功, ç‚¹å‡»å‡çº§æŒ‰é’®å¼€å§‹å‡çº§</span>`
      this.patchFile = { ...file }
      this.fileAdded = true
    },
    onFileAdded() {
      this.patchUpdateStatus = ""
    },
    upgrade() {
      this.upgrading = true
      this.patchUpdateStatus = `<span style="color:red">正在升级...</span>`
      doUpgrade(this.patchFile).then(rsp => {
        this.upgrading = false
        if (rsp && rsp.success) {
          clearTimeout(this.timer)
          this.doneUpgrade()
        }
      }).catch(err => {
        if (err.code) {
          this.upgrading = false
          this.patchUpdateStatus = `<span style="color:red">${err.data}</span>`
          clearTimeout(this.timer)
        } else {
          this.probeServer(this.doneUpgrade)
        }
      })
    },
    doneUpgrade() {
      this.upgrading = false
      this.patchUpdateStatus = `<span style="color:green">升级成功</span>`
      let _this = this
      this.$confirm('升级成功, è¯·é‡æ–°ç™»å½•系统', '成功', {
        type: 'success',
        cancelButtonClass: 'comfirm-class-cancle',
        confirmButtonClass: 'comfirm-class-sure'
      }).then(() => {
        _this.reLogin()
      })
    }
  }
};
</script>
<style lang="scss">
.s-system-manage {
  width: 100% !important;
  min-width: 759px;
  box-sizing: border-box;
  padding: 10px;
  // background-color: #e9ebf2;
  background-color: #fff;
  .s-system-manage-breadcrumb {
    height: 5%;
    box-sizing: border-box;
    border: 1px solid #e4e7ed;
    box-shadow: #e4e7ed 0px 0px 9px inset;
    box-shadow: #e4e7ed 0px 0px 9px inset;
    border-radius: 5px;
  }
  .el-tabs--border-card {
    border: 0px solid #dcdfe6;
    -webkit-box-shadow: none;
    box-shadow: none;
    .el-tabs__header {
      border: 0px solid #dcdfe6;
      .el-tabs__item {
        padding: 5px 50px;
        height: 50px;
        font-family: PingFangSC-Regular;
        font-size: 15px;
        color: #222222;
        text-align: center;
        border: 0px solid transparent;
      }
      .el-tabs__item:nth-child(2) {
        padding-left: 50px !important;
      }
      .el-tabs__item:last-child {
        padding-right: 50px !important;
      }
      .el-tabs__item.is-active {
        color: #3d68e1;
        font-weight: bold;
        // border-right-color: #fff;
        // border-left-color: #fff;
      }
      .el-tabs__item:not(.is-disabled):hover {
        color: #3d68e1;
      }
    }
  }
  .el-tabs__content {
    height: calc(100% - 64px);
    width: calc(100% - 20px);
    box-sizing: border-box;
    overflow-y: auto;
    padding: 10px 40px !important;
    .el-tab-pane {
      width: 100%;
      .s-title {
        text-align: left;
        padding: 15px 0px;
        font-size: 16px;
      }
    }
  }
  .s-table {
    border: 1px solid #e8e8e9;
    margin-top: 40px;
  }
  .ui-top-title {
    padding-bottom: 10px;
    /* border-bottom: 1px solid #ebebeb; */
    position: relative;
    text-align: left;
    padding-left: 15px;
    font-size: 16px;
    font-weight: bold;
  }
  .ui-top-title:before {
    content: " ";
    border-left: 4px solid #f53d3d;
    display: inline-block;
    height: 16px;
    position: absolute;
    top: 50%;
    left: 0;
    margin-top: -13px;
  }
  .el-button--text {
    color: #3d68e1;
    text-decoration: underline;
  }
}
.s-system-maintenance {
  width: 100%;
  height: 100%;
  .el-button--primary {
    color: #ffffff;
    background-color: #3d68e1;
    border-color: #3d68e1;
  }
  .el-button--primary.is-disabled,
  .el-button--primary.is-disabled:hover,
  .el-button--primary.is-disabled:focus,
  .el-button--primary.is-disabled:active {
    color: #ffffff;
    background-color: #9eb4f0;
    border-color: #9eb4f0;
  }
  .box-card {
    text-align: left;
    height: auto;
    margin: 10px 0px;
    .box-card-content {
      padding-bottom: 40px;
      .card-text {
        padding: 0 30px;
        line-height: 32px;
      }
    }
  }
  .upload-icon {
    font-size: 18px;
    color: #0088ff;
  }
  .upload-msg {
    padding-left: 10px;
    text-align: left;
    span {
      line-height: 32px;
      font-size: 13px;
    }
  }
}
.box {
  width: 50%;
  min-width: 700px;
  height: 270px;
  border: 1px solid #eee;
  .title {
    font-size: 20px;
    font-weight: bold;
    text-align: left;
    padding: 20px;
    border-bottom: 1px solid #eee;
  }
  .range {
    width: 100%;
    padding-top: 30px;
    height: 38px;
    .left {
      width: 120px;
      float: left;
      text-align: right;
      font-size: 14px;
      p {
        height: 38px;
        line-height: 38px;
        margin: 0;
      }
    }
    .middle {
      width: 50%;
      min-width: 400px;
      height: 38px;
      float: left;
    }
    .right {
      width: 20%;
      height: 38px;
      float: left;
    }
  }
  .tip {
    width: 100%;
    padding: 30px 0px 0px 30px;
    height: 34px;
    .zhuyi {
      font-size: 14px;
      height: 34px;
      line-height: 34px;
      margin-left: 20px;
      float: left;
    }
    i {
      font-size: 32px;
      color: #e99038;
      float: left;
    }
  }
}
#systemMaintenance {
  .el-tabs__header {
    border: 0px solid #dcdfe6;
    .el-tabs__item {
      padding: 5px 50px;
      height: 50px;
      font-family: PingFangSC-Regular;
      font-size: 14px;
      color: #222222;
      text-align: center;
      border: 0px solid transparent;
    }
    .el-tabs__item:nth-child(2) {
      padding-left: 50px;
    }
    .el-tabs__item:last-child {
      padding-right: 50px;
    }
    .el-tabs__item.is-active {
      color: #ff7733;
      font-weight: bold;
      // border-right-color: #fff;
      // border-left-color: #fff;
    }
    .el-tabs__item:not(.is-disabled):hover {
      color: #ff7733;
    }
  }
  .el-tabs__active-bar {
    background-color: #ff7733;
  }
  .el-tabs__content {
    padding-left: 15px !important;
  }
}
</style>
src/pages/debuggTool/index/main.ts
New file
@@ -0,0 +1,16 @@
import Vue from 'vue';
import App from './App.vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import "@/assets/css/element-variables.scss";
import moment from 'moment';
Vue.use(ElementUI)
Vue.prototype.$moment = moment;
new Vue({
  el: '#app',
  render: h => h(App)
})
src/pages/desktop/index/components/ToolsEntry.vue
@@ -190,6 +190,9 @@
      if (dock.type === "1") {
        window.open(dock.url);
      } else if (dock.type === "2" && !dock.isOpen) {
        if(dock.url== "/view/debuggTool/"){
          return true;
        }
        this.$store.dispatch("desktop/addFrame", {
          id: dock.id,
          icon: dock.src,
src/pages/systemMonitor/api/api.ts
New file
@@ -0,0 +1,22 @@
import request from "@/scripts/httpRequest";
export const showSystemStates = () => {
  return request({
    url: "/data/api-z/system/showSystemStates",
    method: "get"
  });
};
export const showService = () => {
  return request({
    url: "/data/api-z/system/showService",
    method: "get"
  });
};
export const showProcesses = () => {
  return request({
    url: "/data/api-z/system/showProcesses",
    method: "get"
  });
};
src/pages/systemMonitor/index/App.vue
New file
@@ -0,0 +1,853 @@
<template>
  <div class="s-system-monitor">
    <el-tabs id="systemMonitor" v-model="activeName">
      <el-tab-pane label="单元" name="proc">
        <div class="form-title">
          <b>算法单元</b>
          (正在进行{{algoProcessData.length}}个算法单元)
          <el-table :data="algoProcessData" style="width: 100%">
            <el-table-column prop="desc" label="名称" width="180"></el-table-column>
            <el-table-column label="CPU" width="180">
              <template slot-scope="scope">
                <span>{{ scope.row.cpu.toFixed(2)}} %</span>
              </template>
            </el-table-column>
            <el-table-column label="内存">
              <template slot-scope="scope">
                <span>{{ scope.row.mem.toFixed(2)}} %</span>
              </template>
            </el-table-column>
            <el-table-column prop="disk" label="硬盘"></el-table-column>
            <el-table-column label="算力">
              <template slot-scope="scope">
                <span>{{ scope.row.gpu}} M</span>
              </template>
            </el-table-column>
            <el-table-column prop="net" label="网络"></el-table-column>
          </el-table>
        </div>
        <div class="form-title" style="margin-top:20px">
          <b>应用单元</b>
          (正在进行{{appProcessData.length}}个应用单元)
          <el-table :data="appProcessData" style="width: 100%">
            <el-table-column prop="desc" label="名称" width="180"></el-table-column>
            <el-table-column prop="cpu" label="CPU" width="180"></el-table-column>
            <el-table-column prop="mem" label="内存"></el-table-column>
            <el-table-column prop="disk" label="硬盘"></el-table-column>
            <el-table-column prop="gpu" label="算力"></el-table-column>
            <el-table-column prop="net" label="网络"></el-table-column>
          </el-table>
        </div>
      </el-tab-pane>
      <el-tab-pane label="性能" name="top">
        <div class="column-left" ref="left">
          <div class="resize-bar">
            <div
              :class="['ax_default', activeChartItem == 'cpu' ?'selected': '']"
              @click="setActiveChartItem('cpu')"
            >
              <div class="ax_default_pic color-cpu"></div>
              <div class="ax_default_text">CPU</div>
              <div class="ax_default_subtext">{{cpuUsedPercent}}%</div>
            </div>
            <div
              :class="['ax_default', activeChartItem == 'mem' ?'selected': '']"
              @click="setActiveChartItem('mem')"
            >
              <div class="ax_default_pic color-mem"></div>
              <div class="ax_default_text">内存</div>
              <div class="ax_default_subtext">{{memUsedPercent}}%</div>
            </div>
            <div
              :class="['ax_default', activeChartItem == 'gpu' ?'selected': '']"
              @click="setActiveChartItem('gpu')"
            >
              <div class="ax_default_pic color-gpu"></div>
              <div class="ax_default_text">算力</div>
              <div class="ax_default_subtext">{{gpuUsedPercent}}%</div>
            </div>
            <div
              :class="['ax_default', activeChartItem == 'net' ?'selected': '']"
              @click="setActiveChartItem('net')"
            >
              <div class="ax_default_pic color-net"></div>
              <div class="ax_default_text">网络</div>
              <div class="ax_default_subtext">{{netSend | byteConver}} / {{netRecive | byteConver}}</div>
            </div>
            <div
              v-for="(v, k) in disks"
              :key="k"
              :class="['ax_default', activeChartItem == ('disk|' + k) ?'selected': '']"
              @click="setActiveChartItem('disk|'+k)"
            >
              <div class="ax_default_pic color-disk"></div>
              <div class="ax_default_text">磁盘 {{k}}</div>
              <div class="ax_default_subtext">{{v.info.total | byteConver}}</div>
            </div>
          </div>
          <div class="resize-line"></div>
        </div>
        <div class="column-right">
          <div class="max-val">{{yAxisMaxVal}}</div>
          <div ref="graphs" class="graphs-chart"></div>
          <div v-show="activeChartItem == 'cpu'">
            <div class="ax_default_label">
              <b>占用率</b>
              <p>{{cpuUsedPercent}}%</p>
            </div>
            <div class="ax_default_label">
              <b>主频</b>
              <p>{{cpuMaxRate}}Ghz</p>
            </div>
            <div class="ax_default_label">
              <b>插槽</b>
              <p>{{cpuCount}}</p>
            </div>
            <div class="ax_default_label">
              <b>内核</b>
              <p>{{cpuCore}}</p>
            </div>
          </div>
          <div v-show="activeChartItem == 'mem'">
            <div class="ax_default_label">
              <b>使用中</b>
              <p>{{memUsed}}G</p>
            </div>
            <div class="ax_default_label">
              <b>已缓存</b>
              <p>{{memCache}}G</p>
            </div>
            <div class="ax_default_label">
              <b>可用</b>
              <p>{{memFree}}G</p>
            </div>
          </div>
          <div v-show="activeChartItem == 'net'">
            <div class="ax_default_label">
              <b>接收</b>
              <p>{{netRecive |byteConver}}</p>
            </div>
            <div class="ax_default_label">
              <b>发送</b>
              <p>{{netSend |byteConver}}</p>
            </div>
            <div class="ax_default_label">
              <b>IP地址</b>
              <p>{{ipAddr}}</p>
            </div>
            <div class="ax_default_label" style="margin-left: 19px;">
              <b>MAC地址</b>
              <p>{{macAddr}}</p>
            </div>
          </div>
          <div v-show="isDisk">
            <div class="ax_default_label">
              <b>容量</b>
              <p>{{activeDisk.total |byteConver}}</p>
            </div>
            <div class="ax_default_label">
              <b>已用</b>
              <p>{{activeDisk.used |byteConver}}</p>
            </div>
            <div class="ax_default_label">
              <b>可用</b>
              <p>{{activeDisk.free |byteConver}}</p>
            </div>
            <div class="ax_default_label">
              <b>读取速度</b>
              <p>{{ioRead | byteConver}}</p>
            </div>
            <div class="ax_default_label">
              <b>写入速度</b>
              <p>{{ioWrite | byteConver}}</p>
            </div>
          </div>
        </div>
      </el-tab-pane>
      <el-tab-pane label="服务" name="service">
        <el-table :data="vasystemServicesData">
          <el-table-column label="名称" show-overflow-tooltip>
            <template slot-scope="scope">
              <span>{{ scope.row.name}}</span>
            </template>
          </el-table-column>
          <el-table-column label="状态" prop="status">
            <template slot-scope="scope">
              <span>{{ scope.row.pid == 0 ? "未启动" : "已启动" }}</span>
            </template>
          </el-table-column>
          <el-table-column label="描述" prop="desc"></el-table-column>
        </el-table>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script>
import echarts from "echarts";
import { showSystemStates, showService, showProcesses } from "../api/api"
export default {
  components: {
  },
  computed: {
    isDisk() {
      return this.activeChartItem.indexOf("disk") == 0
    },
    isAdmin() {
      if (
        sessionStorage.getItem('userInfo') &&
        sessionStorage.getItem('userInfo') !== ''
      ) {
        let loginName = JSON.parse(sessionStorage.getItem('userInfo')).username
        return (
          loginName === 'superadmin' || loginName === 'basic'
        )
      }
      return false;
    }
  },
  filters: {
    byteConver(limit) {
      var size = "";
      if (limit < 1024) { //如果小于0.1KB转化成B
        size = limit + "B";
      } else if (limit < 1024 * 1024) {//如果小于0.1MB转化成KB
        size = (limit / 1024).toFixed(2) + "KB";
      } else if (limit < 1024 * 1024 * 1024) { //如果小于0.1GB转化成MB
        size = (limit / (1024 * 1024)).toFixed(2) + "MB";
      } else if (limit < 1024 * 1024 * 1024 * 1024) { //其他转化成GB
        size = (limit / (1024 * 1024 * 1024)).toFixed(2) + "GB";
      } else {
        size = (limit / (1024 * 1024 * 1024 * 1024)).toFixed(2) + "TB";
      }
      var sizestr = size + "";
      var len = sizestr.indexOf("\.");
      var dec = sizestr.substr(len + 1, 2);
      if (dec == "00") {//当小数点后为00时 åŽ»æŽ‰å°æ•°éƒ¨åˆ†
        return sizestr.substring(0, len) + sizestr.substr(len + 3, 2);
      }
      return sizestr;
    }
  },
  data() {
    return {
      activeName: "proc",
      activeChartItem: "cpu",
      yAxisMaxVal: "100%",
      algoProcessData: [],
      appProcessData: [],
      vasystemServicesData: [],
      eChartsObj: {},
      eChartsBaseOpt: {
        title: {
          top: 10,
        },
        animation: false,
        grid: {
          show: true,
          left: '1%',
          right: '4%',
          bottom: '3%',
          containLabel: true,
          borderWidth: 2,
          borderColor: '#000'
        },
        xAxis: {
          type: "category",
          boundaryGap: false,
          data: Array.from({ length: 60 }, () => 0),
          // show: false,
          axisLabel: {
            show: false
          },
          axisTick: {
            show: false
          },
          splitLine: {
            show: true,
            interval: 5,
            lineStyle: {
              width: 1,
              type: 'solid'
            }
          }
        },
        yAxis: {
          type: 'value',
          // show: false,
          axisLine: {
            show: false
          },
          axisLabel: {
            show: false
          },
          axisTick: {
            show: false
          }
        },
        series: [
          {
            type: 'line',
            symbol: 'none',
            data: Array.from({ length: 60 }, () => 0),
            // smooth: true,
            itemStyle: {},
            areaStyle: {}
          }
        ]
      },
      cpuUtilizations: Array.from({ length: 60 }, () => 0),
      memUtilizations: Array.from({ length: 60 }, () => 0),
      gpuUtilizations: Array.from({ length: 60 }, () => 0),
      diskIOWriteCount: Array.from({ length: 60 }, () => 0),
      diskIOReadCount: Array.from({ length: 60 }, () => 0),
      netReciveCount: Array.from({ length: 60 }, () => 0),
      netSendCount: Array.from({ length: 60 }, () => 0),
      cpuModel: "",
      cpuUsedPercent: 0,
      cpuMaxRate: 0,
      cpuCount: 0,
      cpuCore: 0,
      memTotal: 0,
      memUsed: 0,
      memFree: 0,
      memCache: 0,
      memUsedPercent: 0,
      netSend: 0,
      netRecive: 0,
      ipAddr: "",
      macAddr: "",
      gpuUsedPercent: 0,
      disks: {},
      activeDisk: {},
      ioRead: 0,
      ioWrite: 0
    };
  },
  mounted() {
    this.dataCollection();
    this.initChart();
  },
  methods: {
    // å¾ªçŽ¯é‡‡é›†æ‰€æœ‰æ•°æ®
    dataCollection() {
      this.serviceCollect();
      this.procCollect();
      setTimeout(() => {
        this.dataCollection();
      }, 5000)
    },
    serviceCollect() {
      showService().then(rsp => {
        if (rsp && rsp.success) {
          rsp.data.sort(function (obj1, obj2) {
            var val1 = obj1.name
            var val2 = obj2.name
            if (val1 < val2) {
              return -1
            } else if (val1 > val2) {
              return 1
            } else {
              return 0
            }
          })
          this.vasystemServicesData = rsp.data;
        }
      }).catch(() => { })
    },
    procCollect() {
      showProcesses().then(rsp => {
        if (rsp && rsp.success) {
          if (rsp.data.algos)
            this.algoProcessData = rsp.data.algos;
          if (rsp.data.apps)
            this.appProcessData = rsp.data.apps;
        }
      }).catch(() => { })
    },
    getSystemState() {
      showSystemStates().then(rsp => {
        if (rsp && rsp.success) {
          // å¤„理cpu
          this.cpuUtilizations = this.cpuUtilizations.slice(1);
          this.cpuUtilizations.push(rsp.data.cpu.toFixed(2));
          this.cpuUsedPercent = this.cpuUtilizations[59];
          if (rsp.data.cpu_info) {
            this.cpuMaxRate = (rsp.data.cpu_info[0].mhz / 1024).toFixed(2);
            this.cpuModel = rsp.data.cpu_info[0].modelName;
            this.cpuCore = Number(rsp.data.cpu_info[rsp.data.cpu_info.length - 1].coreId) + 1;
            this.cpuCount = (rsp.data.cpu_info.length / this.cpuCore).toFixed(0);
          }
          // å†…å­˜
          this.memUtilizations = this.memUtilizations.slice(1);
          this.memUtilizations.push(rsp.data.mem.usedPercent.toFixed(2));
          this.memTotal = (rsp.data.mem.total / 1024 / 1024 / 1000).toFixed(0);
          this.memUsed = (rsp.data.mem.used / 1024 / 1024 / 1000).toFixed(2);
          this.memFree = (rsp.data.mem.free / 1024 / 1024 / 1000).toFixed(2);
          this.memCache = ((rsp.data.mem.cached + rsp.data.mem.buffers) / 1024 / 1024 / 1000).toFixed(2);
          this.memUsedPercent = rsp.data.mem.usedPercent.toFixed(2);
          // ç®—力
          this.gpuUtilizations = this.gpuUtilizations.slice(1);
          this.gpuUtilizations.push(rsp.data.gpu.toFixed(2));
          this.gpuUsedPercent = this.gpuUtilizations[59];
          // ç½‘络
          this.netReciveCount = this.netReciveCount.slice(1);
          this.netReciveCount.push(rsp.data.net.bytesRecv);
          this.netSendCount = this.netSendCount.slice(1);
          this.netSendCount.push(rsp.data.net.bytesSent);
          this.netRecive = this.netReciveCount[59];
          this.netSend = this.netSendCount[59];
          this.macAddr = rsp.data.net.mac;
          if (rsp.data.net.addr.length) {
            this.ipAddr = rsp.data.net.addr[0].addr;
          }
          // ç£ç›˜
          rsp.data.disk.sort(function (obj1, obj2) {
            var val1 = obj1.name;
            var val2 = obj2.name;
            if (val1 < val2) {
              return -1;
            } else if (val1 > val2) {
              return 1;
            } else {
              return 0;
            }
          })
          rsp.data.disk.forEach(d => {
            if (d.name in this.disks) {
              this.disks[d.name].readBytes = this.disks[d.name].readBytes.slice(1);
              this.disks[d.name].readBytes.push(d.readBytes);
              this.disks[d.name].writeBytes = this.disks[d.name].writeBytes.slice(1);
              this.disks[d.name].writeBytes.push(d.writeBytes);
            } else {
              this.disks[d.name] = {};
              this.disks[d.name]["info"] = d.info;
              this.disks[d.name]["readBytes"] = Array.from({ length: 60 }, () => 0);
              this.disks[d.name]["writeBytes"] = Array.from({ length: 60 }, () => 0);
            }
          });
          // this.disks = rsp.data.disk;
        }
        this.setChartData();
        setTimeout(() => {
          this.getSystemState();
        }, 1000)
      })
    },
    isShow(authority) {
      if (this.isAdmin) {
        return true
      } else if (
        this.buttonAuthority.indexOf(',' + authority + ',') > -1
      ) {
        return true
      } else {
        return false
      }
    },
    format(array) {
      return [
        this.$moment(array[0]).format("YYYY-MM-DD"),
        this.$moment(array[1]).format("YYYY-MM-DD")
      ];
    },
    setActiveChartItem(item) {
      this.activeChartItem = item;
      this.eChartsObj.clear();
      this.setChartData();
    },
    initChart() {
      this.eChartsObj = echarts.init(this.$refs.graphs);
      this.getSystemState();
    },
    setChartData() {
      let option = JSON.parse(JSON.stringify(this.eChartsBaseOpt));
      switch (this.activeChartItem) {
        case 'cpu':
          this.yAxisMaxVal = this.cpuModel;
          option.title.text = "CPU";
          option.title.subtext = "%占用率";
          option.yAxis.max = 100;
          option.yAxis.min = 0;
          option.grid.borderColor = "#8aadd0";
          option.series[0].itemStyle.color = "#8aadd0";
          option.series[0].areaStyle.color = "#a8d4ff";
          option.series[0].data = this.cpuUtilizations;
          break;
        case 'mem':
          this.yAxisMaxVal = this.memTotal + "G";
          option.title.text = "内存";
          option.title.subtext = "内存使用量";
          option.yAxis.max = 100;
          option.yAxis.min = 0;
          option.grid.borderColor = "#ff9900";
          option.series[0].itemStyle.color = "#ff9900";
          option.series[0].areaStyle.color = "#f7bb88";
          option.series[0].data = this.memUtilizations;
          break;
        case 'gpu':
          this.yAxisMaxVal = "100%";
          option.title.text = "算力";
          option.title.subtext = "%使用率";
          option.yAxis.max = 100;
          option.yAxis.min = 0;
          option.grid.borderColor = "#bc84d8";
          option.series[0].itemStyle.color = "#bc84d8";
          option.series[0].areaStyle.color = "#de9dff";
          option.series[0].data = this.gpuUtilizations;
          break;
        case 'net':
          this.yAxisMaxVal = "";
          option.title.text = "网络";
          option.title.subtext = "网络负载";
          option.grid.borderColor = "#4696da";
          option.series[0].itemStyle.color = "#ffa16a";
          option.series[0].areaStyle.color = "#d68658";
          option.series[0].data = this.netReciveCount;
          option.series.push({
            type: 'line',
            symbol: 'none',
            data: this.netSendCount,
            // smooth: true,
            itemStyle: {
              color: "#4696da"
            },
            areaStyle: {
              color: "#4eacfd"
            }
          })
          break;
        default:
          if (this.activeChartItem.indexOf("disk|") == 0) {
            let dev = this.activeChartItem.split("|")[1];
            this.yAxisMaxVal = "";
            option.title.text = "磁盘";
            option.title.subtext = "磁盘传输速率";
            option.grid.borderColor = "#33cc66";
            option.series[0].itemStyle.color = "#4696da";
            option.series[0].areaStyle.color = "#4eacfd";
            option.series[0].data = this.disks[dev].readBytes;
            option.series.push({
              type: 'line',
              symbol: 'none',
              data: this.disks[dev].writeBytes,
              // smooth: true,
              itemStyle: {
                color: "#33ff66"
              },
              areaStyle: {
                color: "#33cc66"
              }
            })
            this.activeDisk = this.disks[dev].info;
            this.ioRead = this.disks[dev].readBytes[59];
            this.ioWrite = this.disks[dev].writeBytes[59];
          }
          break;
      }
      this.eChartsObj.setOption(option);
    }
  }
};
</script>
<style lang="scss">
.s-system-monitor {
  width: 100% !important;
  min-width: 759px;
  box-sizing: border-box;
  padding: 10px;
  // background-color: #e9ebf2;
  background-color: #fff;
  .form-title {
    text-align: left;
    font-size: 14px;
  }
  .graphs-chart {
    height: 500px;
    width: 900px;
    margin-left: 15px;
    // border: 1px solid rgb(69, 69, 172);
  }
  .column-left {
    background-color: #fff;
    position: relative;
    float: left;
  }
  .column-right {
    //overflow: hidden;
    overflow-y: hidden;
    overflow-x: auto;
  }
  .max-val {
    position: absolute;
    right: 4%;
    top: 8%;
  }
  .resize-bar {
    width: 238px;
    height: 610px;
  }
  /* æ‹–拽线 */
  .resize-line {
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    border-right: 1px solid #efefef;
    border-left: 1px solid #e0e0e0;
    pointer-events: none;
  }
  .ax_default {
    width: 207px;
    height: 45px;
    padding: 10px;
    font-size: 15px;
    margin: 5px;
    cursor: pointer;
  }
  .selected {
    background: inherit;
    background-color: #cde8ff;
    border: none;
    border-radius: 8px;
  }
  .ax_default_pic {
    position: absolute;
    border-width: 0px;
    width: 66px;
    height: 45px;
    box-sizing: border-box;
    border: 1px solid rgba(255, 153, 0, 1);
    background-repeat: no-repeat;
    background-position: bottom;
  }
  .color-cpu {
    border-color: #8aadd0;
    background-image: url("/images/systemMonitor/cpu.png");
  }
  .color-mem {
    border-color: #ff9900;
    background-image: url("/images/systemMonitor/mem.png");
  }
  .color-gpu {
    border-color: #bc84d8;
    background-image: url("/images/systemMonitor/gpu.png");
  }
  .color-net {
    border-color: #4696da;
    background-image: url("/images/systemMonitor/net.png");
  }
  .color-disk {
    border-color: #33cc66;
    background-image: url("/images/systemMonitor/disk.png");
  }
  .ax_default_text {
    position: relative;
    text-align: left;
    margin-left: 72px;
  }
  .ax_default_subtext {
    font-size: 13px;
    margin-left: 72px;
    line-height: 35px;
    color: #4e4d4d;
    text-align: left;
  }
  .ax_default_label {
    width: 120px;
    font-size: 15px;
    display: inline-block;
    float: left;
    b {
      font-family: "思源黑体";
      font-weight: 400;
      font-style: normal;
      color: #a1a1a1;
    }
    p {
      margin-top: 5px;
    }
  }
  .ax_label_flex {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
  }
  .s-system-monitor-breadcrumb {
    height: 5%;
    box-sizing: border-box;
    border: 1px solid #e4e7ed;
    box-shadow: #e4e7ed 0px 0px 9px inset;
    border-radius: 5px;
  }
  .el-tabs--border-card {
    border: 0px solid #dcdfe6;
    -webkit-box-shadow: none;
    box-shadow: none;
    .el-tabs__header {
      border: 0px solid #dcdfe6;
      .el-tabs__item {
        padding: 5px 50px;
        height: 50px;
        font-family: PingFangSC-Regular;
        font-size: 15px;
        color: #222222;
        text-align: center;
        border: 0px solid transparent;
      }
      .el-tabs__item:nth-child(2) {
        padding-left: 50px !important;
      }
      .el-tabs__item:last-child {
        padding-right: 50px !important;
      }
      .el-tabs__item.is-active {
        color: #3d68e1;
        font-weight: bold;
        // border-right-color: #fff;
        // border-left-color: #fff;
      }
      .el-tabs__item:not(.is-disabled):hover {
        color: #3d68e1;
      }
    }
  }
  .el-tabs__content {
    height: calc(100% - 64px);
    width: calc(100% - 20px);
    box-sizing: border-box;
    overflow-y: auto;
    padding: 10px 10px !important;
    .el-tab-pane {
      width: 100%;
      .s-title {
        text-align: left;
        padding: 15px 0px;
        font-size: 16px;
      }
    }
  }
  .s-table {
    border: 1px solid #e8e8e9;
    margin-top: 40px;
  }
  .ui-top-title {
    padding-bottom: 10px;
    /* border-bottom: 1px solid #ebebeb; */
    position: relative;
    text-align: left;
    padding-left: 15px;
    font-size: 16px;
    font-weight: bold;
  }
  .ui-top-title:before {
    content: " ";
    border-left: 4px solid #f53d3d;
    display: inline-block;
    height: 16px;
    position: absolute;
    top: 50%;
    left: 0;
    margin-top: -13px;
  }
  .el-button--text {
    color: #3d68e1;
    text-decoration: underline;
  }
}
#systemMonitor {
  .el-tabs__header {
    border: 0px solid #dcdfe6;
    .el-tabs__item {
      padding: 5px 50px;
      height: 50px;
      font-family: PingFangSC-Regular;
      font-size: 14px;
      color: #222222;
      text-align: center;
      border: 0px solid transparent;
    }
    .el-tabs__item:nth-child(2) {
      padding-left: 50px;
    }
    .el-tabs__item:last-child {
      padding-right: 50px;
    }
    .el-tabs__item.is-active {
      color: #ff7733;
      font-weight: bold;
      // border-right-color: #fff;
      // border-left-color: #fff;
    }
    .el-tabs__item:not(.is-disabled):hover {
      color: #ff7733;
    }
  }
  .el-tabs__active-bar {
    background-color: #ff7733;
  }
  .el-tabs__content {
    padding-left: 15px !important;
  }
}
</style>
src/pages/systemMonitor/index/main.ts
New file
@@ -0,0 +1,16 @@
import Vue from 'vue';
import App from './App.vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import "@/assets/css/element-variables.scss";
import moment from 'moment';
Vue.use(ElementUI)
Vue.prototype.$moment = moment;
new Vue({
  el: '#app',
  render: h => h(App)
})
src/pages/vindicate/index/App.vue
New file
@@ -0,0 +1,617 @@
<template>
  <div class="s-system-manage">
    <el-tabs
      id="systemMaintenance"
      v-model="activeName"
      v-loading="loading"
      :element-loading-text="loadingText"
    >
      <el-tab-pane label="设备维护" name="device" v-if="isShow('vindicate:device')">
        <div class="s-system-maintenance">
          <div class="box-card">
            <div class="ui-top-view">
              <div class="ui-top-title">重启</div>
            </div>
            <el-divider></el-divider>
            <div class="box-card-content">
              <el-button type="primary" size="small" style="width:80px" @click="reboot">重启</el-button>
              <b class="card-text">重启节点</b>
              <el-row style="margin-top:20px">
                <el-col>
                  <vue-cron :expression="rebootCron" @update="setRebootCron" />
                </el-col>
              </el-row>
            </div>
          </div>
          <!--
          <div class="box-card">
            <div class="ui-top-view">
              <div class="ui-top-title">恢复默认值</div>
            </div>
            <el-divider></el-divider>
            <div class="box-card-content">
              <el-row>
                <el-col :span="1">
                  <el-button type="primary" size="small">简单恢复</el-button>
                </el-col>
                <el-col :span="23">
                  <b class="card-text">简单恢复设备参数</b>
                </el-col>
              </el-row>
              <el-row style="margin-top:20px">
                <el-col :span="1">
                  <el-button type="primary" size="small">完全恢复</el-button>
                </el-col>
                <el-col :span="23">
                  <b class="card-text">完全恢复设备参数到出厂设置</b>
                </el-col>
              </el-row>
            </div>
          </div>
          <div class="box-card">
            <div class="ui-top-view">
              <div class="ui-top-title">参数导入导出</div>
            </div>
            <el-divider></el-divider>
            <div class="box-card-content">
              <el-row :gutter="4">
                <el-col :span="1">
                  <el-button type="info" size="small" style="width:80px">导入</el-button>
                </el-col>
                <el-col :span="3" style="padding-left:30px">
                  <el-input placeholder="上传参数文件" size="small" :readonly="true">
                    <el-upload slot="suffix" action="https://jsonplaceholder.typicode.com/posts/">
                      <el-button
                        type="text"
                        icon="el-icon-upload2"
                        size="small"
                        style="font-size:18px; color:#0088ff"
                      ></el-button>
                    </el-upload>
                  </el-input>
                </el-col>
              </el-row>
              <el-row style="margin-top:20px">
                <el-col :span="1">
                  <el-button type="primary" size="small">设备参数</el-button>
                </el-col>
                <el-col :span="23">
                  <b class="card-text">参数导出</b>
                </el-col>
              </el-row>
            </div>
          </div>
          -->
          <div class="box-card">
            <div class="ui-top-view">
              <div class="ui-top-title">升级</div>
            </div>
          </div>
          <el-divider></el-divider>
          <div class="box-card-content">
            <el-row :gutter="4">
              <el-col :span="12">
                <file-uploader
                  single
                  uploadPlaceholder="上传升级文件"
                  url="/data/api-v/sysset/patchUpdate"
                  @complete="onFileUpload"
                  @file-added="onFileAdded"
                />
              </el-col>
              <el-col :span="2">
                <el-button
                  type="primary"
                  size="small"
                  style="width:80px"
                  @click="upgrade"
                  :disabled="!fileAdded"
                  :loading="upgrading"
                >升级</el-button>
              </el-col>
              <el-col :span="16" class="upload-msg">
                <span v-html="patchUpdateStatus"></span>
              </el-col>
            </el-row>
          </div>
        </div>
      </el-tab-pane>
      <el-tab-pane label="数据库维护" name="dbvdc" v-if="isShow('vindicate:db')">
        <div class="box">
          <p class="title">
            <label>数据清理</label>
          </p>
          <div class="range">
            <div class="left">
              <p>选择数据范围:</p>
            </div>
            <div class="middle">
              <el-date-picker
                v-model="dataRange"
                type="daterange"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                style="height:38px"
                :picker-options="pickerOptions"
              ></el-date-picker>
            </div>
            <div class="right">
              <el-button @click="deleteData" style="height:38px;background:#ff0000;color:white">删除数据</el-button>
            </div>
          </div>
          <div class="tip">
            <i class="iconfont icontishi-zhuyi"></i>
            <p class="zhuyi">请注意,按以上日期范围删除的数据不可恢复,立即生效,请谨慎操作</p>
          </div>
        </div>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script>
import { rebootServer, getDevInfo, getRebootTask, setRebootTask, fileUpload, doUpgrade, deleteDate } from "@/api/system"
import VueCron from "@/components/subComponents/VueCron"
import FileUploader from "@/components/subComponents/FileUpload/index"
export default {
  components: {
    VueCron,
    FileUploader
  },
  data() {
    return {
      timer: null,
      buttonAuthority: sessionStorage.getItem("buttonAuthoritys") || [],
      rebootCron: "",
      activeName: "device",
      restartValue: "不重启",
      restartTimeValue: new Date(2019, 9, 10, 18, 40),
      loading: false,
      loadingText: '',
      probeSum: 0,
      patchUpdateStatus: "",
      dataRange: [
        this.$moment().format("YYYY-MM-DD HH:mm:ss"),
        this.$moment().format("YYYY-MM-DD HH:mm:ss")
      ],
      fileUploadUrl: fileUpload,
      patchFile: {},
      pickerOptions: {
        disabledDate(time) {
          var day = new Date()
          day.setTime(day.getTime() - 24 * 60 * 60 * 1000)
          return time.getTime() > day;
        },
      },
      upgrading: false,
      fileAdded: false,
    };
  },
  mounted() {
    this.getRebootCron()
    if (!this.isShow('vindicate:device')) {
      this.activeName = "dbvdc"
    }
  },
  computed: {
    isAdmin() {
      if (
        sessionStorage.getItem('userInfo') &&
        sessionStorage.getItem('userInfo') !== ''
      ) {
        let loginName = JSON.parse(sessionStorage.getItem('userInfo')).username
        return (
          loginName === 'superadmin' || loginName === 'basic'
        )
      }
      return false;
    }
  },
  methods: {
    isShow(authority) {
      if (this.isAdmin) {
        return true
      } else if (
        this.buttonAuthority.indexOf(',' + authority + ',') > -1
      ) {
        return true
      } else {
        return false
      }
    },
    format(array) {
      return [
        this.$moment(array[0]).format("YYYY-MM-DD"),
        this.$moment(array[1]).format("YYYY-MM-DD")
      ];
    },
    getRebootCron() {
      getRebootTask().then(rsp => {
        this.rebootCron = rsp.data
      })
    },
    setRebootCron(value) {
      this.rebootCron = value
      setRebootTask({ task: value }).then(rsp => {
        if (rsp && rsp.success) {
          this.$notify({
            type: "success",
            message: "配置成功"
          })
        }
      }).catch(err => {
        this.$notify({
          type: "error",
          message: "配置失败"
        })
      })
    },
    reboot() {
      this.$confirm('确定要重启该节点吗?', {
        center: true,
        cancelButtonClass: 'comfirm-class-cancle',
        confirmButtonClass: 'comfirm-class-sure'
      }).then(() => {
        this.loading = true;
        this.loadingText = "智能计算节点正在重启,请耐心等待..."
        rebootServer().then(rsp => {
          this.probeServer(this.reLogin)
        }).catch(err => {
          if (err.status == 400) {
            this.loading = false;
            this.$notify({
              type: "error",
              message: "重启计算节点失败"
            })
          } else {
            this.probeServer(this.reLogin)
          }
        })
      })
    },
    deleteData() {
      var timeRange = this.format(this.dataRange);
      var showStartTime = timeRange[0]
      var showEndTime = timeRange[1]
      console.log("时间:", showStartTime, showEndTime)
      this.$confirm("提示:" + showStartTime + " è‡³ " + showEndTime + " äº§ç”Ÿçš„全部数据将被删除,此操作立即生效,不可恢复,是否删除?", {
        center: true,
        cancelButtonClass: "comfirm-class-cancle",
        confirmButtonClass: "comfirm-class-sure"
      }).then(() => {
        this.loading = true
        this.loadingText = "正在删除数据,请稍候!"
        var param = {
          startTime: showStartTime,
          endTime: showEndTime
        }
        deleteDate(param).then(resp => {
          if (resp.success) {
            this.$message({
              type: "success",
              message: "删除数据成功"
            })
            this.loading = false
          }
        }).catch(err => {
          this.$message({
            type: "error",
            message: "删除数据失败!"
          })
          this.loading = false
        })
      }).catch(() => {
        console.log("取消了!")
      })
    },
    reLogin() {
      this.$router.push("/")
    },
    probeServer(callback) {
      this.probeSum++;
      let _this = this
      if (this.probeSum > 60) {
        this.$confirm('连接服务器失败, è¯·åˆ·æ–°é¡µé¢æˆ–联系管理员', '失败', {
          type: 'error',
          cancelButtonClass: 'comfirm-class-cancle',
          confirmButtonClass: 'comfirm-class-sure'
        }).then(() => {
          // _this.$router.push("/")
          callback()
        })
        return
      }
      this.timer = setTimeout(() => {
        getDevInfo().then(() => {
          // _this.$router.push("/")
          callback()
        }).catch(err => {
          _this.probeServer(callback)
        })
      }, 10000)
    },
    onFileUpload(file) {
      this.patchUpdateStatus = `<span style="color:green">上传成功, ç‚¹å‡»å‡çº§æŒ‰é’®å¼€å§‹å‡çº§</span>`
      this.patchFile = { ...file }
      this.fileAdded = true
    },
    onFileAdded() {
      this.patchUpdateStatus = ""
    },
    upgrade() {
      this.upgrading = true
      this.patchUpdateStatus = `<span style="color:red">正在升级...</span>`
      doUpgrade(this.patchFile).then(rsp => {
        this.upgrading = false
        if (rsp && rsp.success) {
          clearTimeout(this.timer)
          this.doneUpgrade()
        }
      }).catch(err => {
        if (err.code) {
          this.upgrading = false
          this.patchUpdateStatus = `<span style="color:red">${err.data}</span>`
          clearTimeout(this.timer)
        } else {
          this.probeServer(this.doneUpgrade)
        }
      })
    },
    doneUpgrade() {
      this.upgrading = false
      this.patchUpdateStatus = `<span style="color:green">升级成功</span>`
      let _this = this
      this.$confirm('升级成功, è¯·é‡æ–°ç™»å½•系统', '成功', {
        type: 'success',
        cancelButtonClass: 'comfirm-class-cancle',
        confirmButtonClass: 'comfirm-class-sure'
      }).then(() => {
        _this.reLogin()
      })
    }
  }
};
</script>
<style lang="scss">
.s-system-manage {
  width: 100% !important;
  min-width: 759px;
  box-sizing: border-box;
  padding: 10px;
  // background-color: #e9ebf2;
  background-color: #fff;
  .s-system-manage-breadcrumb {
    height: 5%;
    box-sizing: border-box;
    border: 1px solid #e4e7ed;
    box-shadow: #e4e7ed 0px 0px 9px inset;
    box-shadow: #e4e7ed 0px 0px 9px inset;
    border-radius: 5px;
  }
  .el-tabs--border-card {
    border: 0px solid #dcdfe6;
    -webkit-box-shadow: none;
    box-shadow: none;
    .el-tabs__header {
      border: 0px solid #dcdfe6;
      .el-tabs__item {
        padding: 5px 50px;
        height: 50px;
        font-family: PingFangSC-Regular;
        font-size: 15px;
        color: #222222;
        text-align: center;
        border: 0px solid transparent;
      }
      .el-tabs__item:nth-child(2) {
        padding-left: 50px !important;
      }
      .el-tabs__item:last-child {
        padding-right: 50px !important;
      }
      .el-tabs__item.is-active {
        color: #3d68e1;
        font-weight: bold;
        // border-right-color: #fff;
        // border-left-color: #fff;
      }
      .el-tabs__item:not(.is-disabled):hover {
        color: #3d68e1;
      }
    }
  }
  .el-tabs__content {
    height: calc(100% - 64px);
    width: calc(100% - 20px);
    box-sizing: border-box;
    overflow-y: auto;
    padding: 10px 40px !important;
    .el-tab-pane {
      width: 100%;
      .s-title {
        text-align: left;
        padding: 15px 0px;
        font-size: 16px;
      }
    }
  }
  .s-table {
    border: 1px solid #e8e8e9;
    margin-top: 40px;
  }
  .ui-top-title {
    padding-bottom: 10px;
    /* border-bottom: 1px solid #ebebeb; */
    position: relative;
    text-align: left;
    padding-left: 15px;
    font-size: 16px;
    font-weight: bold;
  }
  .ui-top-title:before {
    content: " ";
    border-left: 4px solid #f53d3d;
    display: inline-block;
    height: 16px;
    position: absolute;
    top: 50%;
    left: 0;
    margin-top: -13px;
  }
  .el-button--text {
    color: #3d68e1;
    text-decoration: underline;
  }
}
.s-system-maintenance {
  width: 100%;
  height: 100%;
  .el-button--primary {
    color: #ffffff;
    background-color: #3d68e1;
    border-color: #3d68e1;
  }
  .el-button--primary.is-disabled,
  .el-button--primary.is-disabled:hover,
  .el-button--primary.is-disabled:focus,
  .el-button--primary.is-disabled:active {
    color: #ffffff;
    background-color: #9eb4f0;
    border-color: #9eb4f0;
  }
  .box-card {
    text-align: left;
    height: auto;
    margin: 10px 0px;
    .box-card-content {
      padding-bottom: 40px;
      .card-text {
        padding: 0 30px;
        line-height: 32px;
      }
    }
  }
  .upload-icon {
    font-size: 18px;
    color: #0088ff;
  }
  .upload-msg {
    padding-left: 10px;
    text-align: left;
    span {
      line-height: 32px;
      font-size: 13px;
    }
  }
}
.box {
  width: 50%;
  min-width: 700px;
  height: 270px;
  border: 1px solid #eee;
  .title {
    font-size: 20px;
    font-weight: bold;
    text-align: left;
    padding: 20px;
    border-bottom: 1px solid #eee;
  }
  .range {
    width: 100%;
    padding-top: 30px;
    height: 38px;
    .left {
      width: 120px;
      float: left;
      text-align: right;
      font-size: 14px;
      p {
        height: 38px;
        line-height: 38px;
        margin: 0;
      }
    }
    .middle {
      width: 50%;
      min-width: 400px;
      height: 38px;
      float: left;
    }
    .right {
      width: 20%;
      height: 38px;
      float: left;
    }
  }
  .tip {
    width: 100%;
    padding: 30px 0px 0px 30px;
    height: 34px;
    .zhuyi {
      font-size: 14px;
      height: 34px;
      line-height: 34px;
      margin-left: 20px;
      float: left;
    }
    i {
      font-size: 32px;
      color: #e99038;
      float: left;
    }
  }
}
#systemMaintenance {
  .el-tabs__header {
    border: 0px solid #dcdfe6;
    .el-tabs__item {
      padding: 5px 50px;
      height: 50px;
      font-family: PingFangSC-Regular;
      font-size: 14px;
      color: #222222;
      text-align: center;
      border: 0px solid transparent;
    }
    .el-tabs__item:nth-child(2) {
      padding-left: 50px;
    }
    .el-tabs__item:last-child {
      padding-right: 50px;
    }
    .el-tabs__item.is-active {
      color: #ff7733;
      font-weight: bold;
      // border-right-color: #fff;
      // border-left-color: #fff;
    }
    .el-tabs__item:not(.is-disabled):hover {
      color: #ff7733;
    }
  }
  .el-tabs__active-bar {
    background-color: #ff7733;
  }
  .el-tabs__content {
    padding-left: 15px !important;
  }
}
</style>
src/pages/vindicate/index/main.ts
New file
@@ -0,0 +1,16 @@
import Vue from 'vue';
import App from './App.vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import "@/assets/css/element-variables.scss";
import moment from 'moment';
Vue.use(ElementUI)
Vue.prototype.$moment = moment;
new Vue({
  el: '#app',
  render: h => h(App)
})