Jacky %!s(int64=4) %!d(string=hai) anos
pai
achega
edc412a8e5
Modificáronse 28 ficheiros con 187 adicións e 954 borrados
  1. 2 0
      .gitignore
  2. 2 1
      nginx-ui-frontend/package.json
  3. BIN=BIN
      nginx-ui-frontend/public/favicon.ico
  4. BIN=BIN
      nginx-ui-frontend/src/assets/img/logo.png
  5. 36 0
      nginx-ui-frontend/src/components/Chart/LineChart.vue
  6. 0 62
      nginx-ui-frontend/src/components/Charts/Bar.vue
  7. 0 110
      nginx-ui-frontend/src/components/Charts/ChartCard.vue
  8. 0 67
      nginx-ui-frontend/src/components/Charts/Liquid.vue
  9. 0 51
      nginx-ui-frontend/src/components/Charts/MiniArea.vue
  10. 0 37
      nginx-ui-frontend/src/components/Charts/MiniBar.vue
  11. 0 75
      nginx-ui-frontend/src/components/Charts/MiniProgress.vue
  12. 0 39
      nginx-ui-frontend/src/components/Charts/MiniSmoothArea.vue
  13. 0 68
      nginx-ui-frontend/src/components/Charts/Radar.vue
  14. 0 77
      nginx-ui-frontend/src/components/Charts/RankList.vue
  15. 0 113
      nginx-ui-frontend/src/components/Charts/TagCloud.vue
  16. 0 64
      nginx-ui-frontend/src/components/Charts/TransferBar.vue
  17. 0 87
      nginx-ui-frontend/src/components/Charts/Trend.vue
  18. 0 13
      nginx-ui-frontend/src/components/Charts/chart.less
  19. 0 28
      nginx-ui-frontend/src/components/Charts/index.js
  20. 0 4
      nginx-ui-frontend/src/components/Charts/smooth.area.less
  21. 4 12
      nginx-ui-frontend/src/components/Logo/Logo.vue
  22. 0 8
      nginx-ui-frontend/src/lib/http/index.js
  23. 1 1
      nginx-ui-frontend/src/router/index.js
  24. 3 3
      nginx-ui-frontend/src/views/About.vue
  25. 88 31
      nginx-ui-frontend/src/views/DashBoard.vue
  26. 1 1
      nginx-ui-frontend/vue.config.js
  27. 44 2
      nginx-ui-frontend/yarn.lock
  28. 6 0
      server/api/analytic.go

+ 2 - 0
.gitignore

@@ -5,3 +5,5 @@ database.db
 server/tmp/main
 node_modules
 dist
+nginx-ui-frontend/.env.development
+nginx-ui-frontend/.env.production

+ 2 - 1
nginx-ui-frontend/package.json

@@ -10,9 +10,10 @@
     "dependencies": {
         "ant-design-vue": "^1.7.3",
         "axios": "^0.21.1",
+        "chart.js": "^2.9.4",
         "core-js": "^3.9.0",
-        "viser-vue": "^2.4.8",
         "vue": "^2.6.11",
+        "vue-chartjs": "^3.5.1",
         "vue-itextarea": "^1.0.9",
         "vue-router": "^3.5.1",
         "vuex": "^3.6.2",

BIN=BIN
nginx-ui-frontend/public/favicon.ico


BIN=BIN
nginx-ui-frontend/src/assets/img/logo.png


+ 36 - 0
nginx-ui-frontend/src/components/Chart/LineChart.vue

@@ -0,0 +1,36 @@
+<script>
+import { Line, mixins } from 'vue-chartjs'
+const { reactiveProp } = mixins
+
+export default {
+    name: "LineChart",
+    extends: Line,
+    mixins: [reactiveProp],
+    props: ['options'],
+    data() {
+      return {
+          updating: false
+      }
+    },
+    mounted () {
+        this.renderChart(this.chartData, this.options)
+    },
+    watch: {
+        chartData: {
+            deep: true,
+            handler () {
+                if (!this.updating && this.$data && this.$data._chart) {
+                    // Update the chart
+                    this.updating = true
+                    this.$data._chart.update()
+                    this.$nextTick(() => this.updating = false)
+                }
+            }
+        }
+    }
+}
+</script>
+
+<style lang="less" scoped>
+
+</style>

+ 0 - 62
nginx-ui-frontend/src/components/Charts/Bar.vue

@@ -1,62 +0,0 @@
-<template>
-  <div :style="{ padding: '0 0 32px 32px' }">
-    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
-    <v-chart
-      height="254"
-      :data="data"
-      :forceFit="true"
-      :padding="['auto', 'auto', '40', '50']">
-      <v-tooltip />
-      <v-axis />
-      <v-bar position="x*y"/>
-    </v-chart>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'Bar',
-  props: {
-    title: {
-      type: String,
-      default: ''
-    },
-    data: {
-      type: Array,
-      default: () => {
-        return []
-      }
-    },
-    scale: {
-      type: Array,
-      default: () => {
-        return [{
-          dataKey: 'x',
-          min: 2
-        }, {
-          dataKey: 'y',
-          title: '时间',
-          min: 1,
-          max: 22
-        }]
-      }
-    },
-    tooltip: {
-      type: Array,
-      default: () => {
-        return [
-          'x*y',
-          (x, y) => ({
-            name: x,
-            value: y
-          })
-        ]
-      }
-    }
-  },
-  data () {
-    return {
-    }
-  }
-}
-</script>

+ 0 - 110
nginx-ui-frontend/src/components/Charts/ChartCard.vue

@@ -1,110 +0,0 @@
-<template>
-  <a-card :loading="loading" :body-style="{ padding: '20px 24px 8px' }" :bordered="false">
-    <div class="chart-card-header">
-      <a-statistic :title="title" :precision="precision" :value="total" />
-    </div>
-    <div class="chart-card-content">
-      <div class="content-fix">
-        <slot></slot>
-      </div>
-    </div>
-    <div class="chart-card-footer">
-      <div class="field">
-        <slot name="footer"></slot>
-      </div>
-    </div>
-  </a-card>
-</template>
-
-<script>
-export default {
-  name: 'ChartCard',
-  props: {
-    title: {
-      type: String,
-      default: ''
-    },
-    total: {
-      type: [Function, Number, String],
-      required: false,
-      default: null
-    },
-    loading: {
-      type: Boolean,
-      default: false
-    },
-    precision: {
-      type: Number,
-      default: 0
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .chart-card-header {
-    position: relative;
-    overflow: hidden;
-    width: 100%;
-
-    .meta {
-      position: relative;
-      overflow: hidden;
-      width: 100%;
-      color: rgba(0, 0, 0, .45);
-      font-size: 14px;
-      line-height: 22px;
-    }
-  }
-
-  .chart-card-action {
-    cursor: pointer;
-    position: absolute;
-    top: 0;
-    right: 0;
-  }
-
-  .chart-card-footer {
-    border-top: 1px solid #e8e8e8;
-    padding-top: 9px;
-    margin-top: 8px;
-
-    > * {
-      position: relative;
-    }
-
-    .field {
-      white-space: nowrap;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      margin: 0;
-    }
-  }
-
-  .chart-card-content {
-    margin-bottom: 12px;
-    position: relative;
-    height: 46px;
-    width: 100%;
-
-    .content-fix {
-      position: absolute;
-      left: 0;
-      bottom: 0;
-      width: 100%;
-    }
-  }
-
-  .total {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    word-break: break-all;
-    white-space: nowrap;
-    color: #000;
-    margin-top: 4px;
-    margin-bottom: 0;
-    font-size: 30px;
-    line-height: 38px;
-    height: 38px;
-  }
-</style>

+ 0 - 67
nginx-ui-frontend/src/components/Charts/Liquid.vue

@@ -1,67 +0,0 @@
-<template>
-  <div>
-    <v-chart
-      :forceFit="true"
-      :height="height"
-      :width="width"
-      :data="data"
-      :scale="scale"
-      :padding="0">
-      <v-tooltip />
-      <v-interval
-        :shape="['liquid-fill-gauge']"
-        position="transfer*value"
-        color=""
-        :v-style="{
-          lineWidth: 10,
-          opacity: 0.75
-        }"
-        :tooltip="[
-          'transfer*value',
-          (transfer, value) => {
-            return {
-              name: transfer,
-              value,
-            };
-          },
-        ]"
-      ></v-interval>
-      <v-guide
-        v-for="(row, index) in data"
-        :key="index"
-        type="text"
-        :top="true"
-        :position="{
-          gender: row.transfer,
-          value: 45
-        }"
-        :content="row.value + '%'"
-        :v-style="{
-          fontSize: 100,
-          textAlign: 'center',
-          opacity: 0.75,
-        }"
-      />
-    </v-chart>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'Liquid',
-  props: {
-    height: {
-      type: Number,
-      default: 0
-    },
-    width: {
-      type: Number,
-      default: 0
-    }
-  }
-}
-</script>
-
-<style scoped>
-
-</style>

+ 0 - 51
nginx-ui-frontend/src/components/Charts/MiniArea.vue

@@ -1,51 +0,0 @@
-<template>
-  <div class="antv-chart-mini">
-    <div class="chart-wrapper" :style="{ height: 46 }">
-      <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 0, 18, 0]">
-        <v-tooltip/>
-        <v-smooth-area position="x*y"/>
-      </v-chart>
-    </div>
-  </div>
-</template>
-
-<script>
-  const tooltip = [
-    'x*y',
-    (x, y) => ({
-      name: x,
-      value: y
-    })
-  ]
-  const scale = [{
-    dataKey: 'x',
-    min: 2
-  }, {
-    dataKey: 'y',
-    title: '时间',
-    min: 1,
-    max: 22
-  }]
-
-  export default {
-    name: 'MiniArea',
-    props: {
-      data: {
-        type: Array,
-        default: null
-      },
-    },
-    data() {
-      return {
-        dataConfig: this.data,
-        tooltip,
-        scale,
-        height: 100
-      }
-    }
-  }
-</script>
-
-<style lang="less" scoped>
-  @import "chart";
-</style>

+ 0 - 37
nginx-ui-frontend/src/components/Charts/MiniBar.vue

@@ -1,37 +0,0 @@
-<template>
-  <div class="antv-chart-mini">
-    <div class="chart-wrapper" :style="{ height: 46 }">
-      <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 5, 18, 5]">
-        <v-tooltip/>
-        <v-bar position="x*y"/>
-      </v-chart>
-    </div>
-  </div>
-</template>
-
-<script>
-  const tooltip = [
-    'x*y',
-    (x, y) => ({
-      name: x,
-      value: y
-    })
-  ]
-
-  export default {
-    name: 'MiniBar',
-    props: {
-      data: Array,
-    },
-    data() {
-      return {
-        tooltip,
-        height: 100
-      }
-    }
-  }
-</script>
-
-<style lang="less" scoped>
-  @import "chart";
-</style>

+ 0 - 75
nginx-ui-frontend/src/components/Charts/MiniProgress.vue

@@ -1,75 +0,0 @@
-<template>
-  <div class="chart-mini-progress">
-    <div class="target" :style="{ left: target + '%'}">
-      <span :style="{ backgroundColor: color }" />
-      <span :style="{ backgroundColor: color }"/>
-    </div>
-    <div class="progress-wrapper">
-      <div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'MiniProgress',
-  props: {
-    target: {
-      type: Number,
-      default: 0
-    },
-    height: {
-      type: String,
-      default: '10px'
-    },
-    color: {
-      type: String,
-      default: '#13C2C2'
-    },
-    percentage: {
-      type: Number,
-      default: 0
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .chart-mini-progress {
-    padding: 5px 0;
-    position: relative;
-    width: 100%;
-
-    .target {
-      position: absolute;
-      top: 0;
-      bottom: 0;
-
-      span {
-        border-radius: 100px;
-        position: absolute;
-        top: 0;
-        left: 0;
-        height: 4px;
-        width: 2px;
-
-        &:last-child {
-          top: auto;
-          bottom: 0;
-        }
-      }
-    }
-    .progress-wrapper {
-      background-color: #f5f5f5;
-      position: relative;
-
-      .progress {
-        transition: all .4s cubic-bezier(.08,.82,.17,1) 0s;
-        border-radius: 1px 0 0 1px;
-        background-color: #1890ff;
-        width: 0;
-        height: 100%;
-      }
-    }
-  }
-</style>

+ 0 - 39
nginx-ui-frontend/src/components/Charts/MiniSmoothArea.vue

@@ -1,39 +0,0 @@
-<template>
-  <div :class="prefixCls">
-    <div class="chart-wrapper" :style="{ height: 46 }">
-      <v-chart :force-fit="true" :height="100" :data="dataSource" :scale="scale" :padding="[36, 0, 18, 0]">
-        <v-tooltip />
-        <v-smooth-line position="x*y" :size="1" />
-        <v-smooth-area position="x*y" />
-      </v-chart>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'MiniSmoothArea',
-  props: {
-    prefixCls: {
-      type: String,
-      default: 'ant-pro-smooth-area'
-    },
-    scale: {
-      type: [Object, Array],
-    },
-    dataSource: {
-      type: Array,
-      required: true
-    }
-  },
-  data () {
-    return {
-      height: 100
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  @import "smooth.area.less";
-</style>

+ 0 - 68
nginx-ui-frontend/src/components/Charts/Radar.vue

@@ -1,68 +0,0 @@
-<template>
-  <v-chart :forceFit="true" height="400" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
-    <v-tooltip></v-tooltip>
-    <v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid" />
-    <v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid" />
-    <v-legend dataKey="user" marker="circle" :offset="30" />
-    <v-coord type="polar" radius="0.8" />
-    <v-line position="item*score" color="user" :size="2" />
-    <v-point position="item*score" color="user" :size="4" shape="circle" />
-  </v-chart>
-</template>
-
-<script>
-const axis1Opts = {
-  dataKey: 'item',
-  line: null,
-  tickLine: null,
-  grid: {
-    lineStyle: {
-      lineDash: null
-    },
-    hideFirstLine: false
-  }
-}
-const axis2Opts = {
-  dataKey: 'score',
-  line: null,
-  tickLine: null,
-  grid: {
-    type: 'polygon',
-    lineStyle: {
-      lineDash: null
-    }
-  }
-}
-
-const scale = [
-  {
-    dataKey: 'score',
-    min: 0,
-    max: 80
-  }, {
-    dataKey: 'user',
-    alias: '类型'
-  }
-]
-
-export default {
-  name: 'Radar',
-  props: {
-    data: {
-      type: Array,
-      default: null
-    }
-  },
-  data () {
-    return {
-      axis1Opts,
-      axis2Opts,
-      scale
-    }
-  }
-}
-</script>
-
-<style scoped>
-
-</style>

+ 0 - 77
nginx-ui-frontend/src/components/Charts/RankList.vue

@@ -1,77 +0,0 @@
-<template>
-  <div class="rank">
-    <h4 class="title">{{ title }}</h4>
-    <ul class="list">
-      <li :key="index" v-for="(item, index) in list">
-        <span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
-        <span>{{ item.name }}</span>
-        <span>{{ item.total }}</span>
-      </li>
-    </ul>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'RankList',
-  // ['title', 'list']
-  props: {
-    title: {
-      type: String,
-      default: ''
-    },
-    list: {
-      type: Array,
-      default: null
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-
-  .rank {
-    padding: 0 32px 32px 72px;
-
-    .list {
-      margin: 25px 0 0;
-      padding: 0;
-      list-style: none;
-
-      li {
-        margin-top: 16px;
-
-        span {
-          color: rgba(0, 0, 0, .65);
-          font-size: 14px;
-          line-height: 22px;
-
-          &:first-child {
-            background-color: #f5f5f5;
-            border-radius: 20px;
-            display: inline-block;
-            font-size: 12px;
-            font-weight: 600;
-            margin-right: 24px;
-            height: 20px;
-            line-height: 20px;
-            width: 20px;
-            text-align: center;
-          }
-          &.active {
-            background-color: #314659;
-            color: #fff;
-          }
-          &:last-child {
-            float: right;
-          }
-        }
-      }
-    }
-  }
-
-  .mobile .rank {
-    padding: 0 32px 32px 32px;
-  }
-
-</style>

+ 0 - 113
nginx-ui-frontend/src/components/Charts/TagCloud.vue

@@ -1,113 +0,0 @@
-<template>
-  <v-chart :width="width" :height="height" :padding="[0]" :data="data" :scale="scale">
-    <v-tooltip :show-title="false" />
-    <v-coord type="rect" direction="TL" />
-    <v-point position="x*y" color="category" shape="cloud" tooltip="value*category" />
-  </v-chart>
-</template>
-
-<script>
-import { registerShape } from 'viser-vue'
-const DataSet = require('@antv/data-set')
-
-const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png'
-
-const scale = [
-  { dataKey: 'x', nice: false },
-  { dataKey: 'y', nice: false }
-]
-
-registerShape('point', 'cloud', {
-  draw (cfg, container) {
-    return container.addShape('text', {
-      attrs: {
-        fillOpacity: cfg.opacity,
-        fontSize: cfg.origin._origin.size,
-        rotate: cfg.origin._origin.rotate,
-        text: cfg.origin._origin.text,
-        textAlign: 'center',
-        fontFamily: cfg.origin._origin.font,
-        fill: cfg.color,
-        textBaseline: 'Alphabetic',
-        ...cfg.style,
-        x: cfg.x,
-        y: cfg.y
-      }
-    })
-  }
-})
-
-export default {
-  name: 'TagCloud',
-  props: {
-    tagList: {
-      type: Array,
-      required: true
-    },
-    height: {
-      type: Number,
-      default: 400
-    },
-    width: {
-      type: Number,
-      default: 640
-    }
-  },
-  data () {
-    return {
-      data: [],
-      scale
-    }
-  },
-  watch: {
-    tagList: function (val) {
-      if (val.length > 0) {
-        this.initTagCloud(val)
-      }
-    }
-  },
-  mounted () {
-    if (this.tagList.length > 0) {
-      this.initTagCloud(this.tagList)
-    }
-  },
-  methods: {
-    initTagCloud (dataSource) {
-      const { height, width } = this
-
-      const dv = new DataSet.View().source(dataSource)
-      const range = dv.range('value')
-      const min = range[0]
-      const max = range[1]
-      const imageMask = new Image()
-      imageMask.crossOrigin = ''
-      imageMask.src = imgUrl
-      imageMask.onload = () => {
-        dv.transform({
-          type: 'tag-cloud',
-          fields: ['name', 'value'],
-          size: [width, height],
-          imageMask,
-          font: 'Verdana',
-          padding: 0,
-          timeInterval: 5000, // max execute time
-          rotate () {
-            let random = ~~(Math.random() * 4) % 4
-            if (random === 2) {
-              random = 0
-            }
-            return random * 90 // 0, 90, 270
-          },
-          fontSize (d) {
-            if (d.value) {
-              return ((d.value - min) / (max - min)) * (32 - 8) + 8
-            }
-            return 0
-          }
-        })
-        this.data = dv.rows
-      }
-    }
-  }
-}
-</script>

+ 0 - 64
nginx-ui-frontend/src/components/Charts/TransferBar.vue

@@ -1,64 +0,0 @@
-<template>
-  <div :style="{ padding: '0 0 32px 32px' }">
-    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
-    <v-chart
-      height="254"
-      :data="data"
-      :scale="scale"
-      :forceFit="true"
-      :padding="['auto', 'auto', '40', '50']">
-      <v-tooltip />
-      <v-axis />
-      <v-bar position="x*y"/>
-    </v-chart>
-  </div>
-</template>
-
-<script>
-const tooltip = [
-  'x*y',
-  (x, y) => ({
-    name: x,
-    value: y
-  })
-]
-const scale = [{
-  dataKey: 'x',
-  title: '日期(天)',
-  alias: '日期(天)',
-  min: 2
-}, {
-  dataKey: 'y',
-  title: '流量(Gb)',
-  alias: '流量(Gb)',
-  min: 1
-}]
-
-export default {
-  name: 'Bar',
-  props: {
-    title: {
-      type: String,
-      default: ''
-    }
-  },
-  data () {
-    return {
-      data: [],
-      scale,
-      tooltip
-    }
-  },
-  created () {
-    this.getMonthBar()
-  },
-  methods: {
-    getMonthBar () {
-      this.$http.get('/analysis/month-bar')
-        .then(res => {
-          this.data = res.result
-        })
-    }
-  }
-}
-</script>

+ 0 - 87
nginx-ui-frontend/src/components/Charts/Trend.vue

@@ -1,87 +0,0 @@
-<template>
-  <div class="chart-trend">
-    {{ term }}
-    <span>{{ rate }}%</span>
-    <span :class="['trend-icon', trend]"><a-icon :type="'caret-' + trend"/></span>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'Trend',
-  props: {
-    term: {
-      type: String,
-      default: '',
-      required: true
-    },
-    percentage: {
-      type: Number,
-      default: null
-    },
-    type: {
-      type: Boolean,
-      default: null
-    },
-    target: {
-      type: Number,
-      default: 0
-    },
-    value: {
-      type: Number,
-      default: 0
-    },
-    fixed: {
-      type: Number,
-      default: 2
-    }
-  },
-  data () {
-    return {
-      trend: this.type && 'up' || 'down',
-      rate: this.percentage
-    }
-  },
-  created () {
-    const type = this.type === null ? this.value >= this.target : this.type
-    this.trend = type ? 'up' : 'down'
-    this.rate = Math.abs(this.percentage)
-  },
-  watch: {
-    percentage() {
-      this.rate = Math.abs(this.percentage)
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  .chart-trend {
-    display: inline-block;
-    font-size: 14px;
-    line-height: 22px;
-
-    .trend-icon {
-      font-size: 12px;
-
-      &.up, &.down {
-        margin-left: 4px;
-        position: relative;
-        top: 1px;
-
-        i {
-          font-size: 12px;
-          transform: scale(.83);
-        }
-      }
-
-      &.up {
-        color: #f5222d;
-      }
-      &.down {
-        color: #52c41a;
-        top: -1px;
-      }
-    }
-  }
-</style>

+ 0 - 13
nginx-ui-frontend/src/components/Charts/chart.less

@@ -1,13 +0,0 @@
-.antv-chart-mini {
-  position: relative;
-  width: 100%;
-
-  .chart-wrapper {
-    position: absolute;
-    bottom: -28px;
-    width: 100%;
-
-/*    margin: 0 -5px;
-    overflow: hidden;*/
-  }
-}

+ 0 - 28
nginx-ui-frontend/src/components/Charts/index.js

@@ -1,28 +0,0 @@
-import Bar from './Bar'
-import ChartCard from './ChartCard'
-import Liquid from './Liquid'
-import MiniArea from './MiniArea'
-import MiniBar from './MiniBar'
-import MiniProgress from './MiniProgress'
-import MiniSelect from 'ant-design-vue/lib/pagination/MiniSelect'
-import MiniSmoothArea from './MiniSmoothArea'
-import Radar from './Radar'
-import RankList from './RankList'
-import TransferBar from './TransferBar'
-import Trend from './Trend'
-
-
-export {
-  Bar,
-  ChartCard,
-  Liquid,
-  MiniSmoothArea,
-  MiniSelect,
-  MiniProgress,
-  MiniArea,
-  MiniBar,
-  Radar,
-  RankList,
-  TransferBar,
-  Trend
-}

+ 0 - 4
nginx-ui-frontend/src/components/Charts/smooth.area.less

@@ -1,4 +0,0 @@
-.ant-pro-smooth-area {
-    position: relative;
-    width: 100%;
-}

+ 4 - 12
nginx-ui-frontend/src/components/Logo/Logo.vue

@@ -1,19 +1,13 @@
 <template>
     <div class="logo">
-        <img :src="logo"/>
-        <div class="text">Nginx UI</div>
+        <p class="text">Nginx UI</p>
         <div class="clear"></div>
     </div>
 </template>
 
 <script>
 export default {
-    name: 'Logo',
-    data() {
-        return {
-            logo: require('@/assets/img/logo.png')
-        }
-    }
+    name: 'Logo'
 }
 </script>
 
@@ -33,13 +27,11 @@ export default {
     }
 
     .text {
-        float: left;
         font-weight: 300;
-        font-size: 22px;
+        font-size: 23px;
         line-height: 48px;
         height: 48px;
-        display: inline-block;
-        margin-left: 13px;
+        text-align: center;
     }
 }
 </style>

+ 0 - 8
nginx-ui-frontend/src/lib/http/index.js

@@ -1,5 +1,4 @@
 import axios from 'axios'
-import store from '../store'
 
 /* 创建 axios 实例 */
 let http = axios.create({
@@ -33,13 +32,6 @@ http.interceptors.response.use(
     },
     async error => {
         console.log(error)
-        switch (error.response.status) {
-            case 401:
-            case 403:
-                // 无权访问时,直接登出
-                await store.dispatch('logout')
-                break
-        }
         return Promise.reject(error.response.data)
     }
 )

+ 1 - 1
nginx-ui-frontend/src/router/index.js

@@ -9,7 +9,7 @@ export const routes = [
         path: '/',
         name: '首页',
         component: () => import('@/layouts/BaseLayout'),
-        redirect: '/domain',
+        redirect: '/dashboard',
         children: [
             {
                 path: 'dashboard',

+ 3 - 3
nginx-ui-frontend/src/views/About.vue

@@ -4,13 +4,13 @@
         <p>Yet another WebUI for Nginx</p>
         <p>Version: {{ version }}-{{ build_id }}</p>
         <h3>项目组</h3>
-        <p>前端:0xJacky</p>
-        <p>后端:0xJacky</p>
+        <p>Designer:<a href="https://jackyu.cn/">@0xJacky</a></p>
         <h3>技术栈</h3>
         <p>Go</p>
         <p>Gin</p>
         <p>Vue</p>
-
+        <h3>开源协议</h3>
+        <p>GNU General Public License v2.0</p>
         <p>Copyright © 2020 - {{ this_year }} 0xJacky </p>
     </a-card>
 </template>

+ 88 - 31
nginx-ui-frontend/src/views/DashBoard.vue

@@ -2,7 +2,7 @@
     <div>
         <a-row class="row-two">
             <a-col :lg="24" :sm="24">
-                <a-card style="min-height: 250px" title="后端服务器实时数据">
+                <a-card style="min-height: 400px" title="服务器状态">
                     <a-row>
                         <a-col :lg="12" :sm="24" class="chart">
                             <a-statistic :value="cpu" style="margin: 0 50px 10px 0" title="CPU">
@@ -10,23 +10,27 @@
                                     <span>%</span>
                                 </template>
                             </a-statistic>
-                            <mini-smooth-area :data-source="cpu_analytic"/>
+                            <p>运行时间 {{ uptime }}</p>
+                            <p>系统负载 1min:{{ loadavg.Loadavg1 }}  5min:{{ loadavg.Loadavg5 }}
+                                15min:{{ loadavg.Loadavg15 }}</p>
+                            <line-chart :chart-data="cpu_analytic" :options="cpu_analytic.options" :height="150"/>
                         </a-col>
-                        <a-col :lg="6" :sm="10" class="chart">
-                            <span>实际内存占用</span>
+                        <a-col :lg="6" :sm="8" :xs="12" class="chart_dashboard">
                             <div>
                                 <a-tooltip
-                                    :title="'已使用: '+ memory_used + ' / 总共: ' + memory_total">
-                                    <a-progress :percent="memory_pressure" strokeColor="rgb(135, 208, 104)" type="circle"/>
+                                    :title="'已使用: '+ memory_used + ' 缓存: ' + memory_cached + '  空闲:' + memory_free +
+                                     '  物理内存: ' + memory_total">
+                                    <a-progress :percent="memory_pressure" strokeColor="rgb(135, 208, 104)" type="dashboard" />
+                                    <p class="description">实际内存占用</p>
                                 </a-tooltip>
                             </div>
                         </a-col>
-                        <a-col :lg="6" :sm="10" class="chart">
-                            <span>存储空间</span>
+                        <a-col :lg="6" :sm="8" :xs="12" class="chart_dashboard">
                             <div>
                                 <a-tooltip
                                     :title="'已使用: '+ disk_used + ' / 总共: ' + disk_total">
-                                    <a-progress :percent="disk_percentage" type="circle"/>
+                                    <a-progress :percent="disk_percentage" type="dashboard" />
+                                    <p class="description">存储空间</p>
                                 </a-tooltip>
                             </div>
                         </a-col>
@@ -38,16 +42,12 @@
 </template>
 
 <script>
-import MiniSmoothArea from '@/components/Charts/MiniSmoothArea'
-import Vue from 'vue'
-import Viser from 'viser-vue'
-
-Vue.use(Viser)
+import LineChart from "@/components/Chart/LineChart"
 
 export default {
     name: "DashBoard",
     components: {
-        MiniSmoothArea
+        LineChart
     },
     data() {
         return {
@@ -56,12 +56,58 @@ export default {
             stat: {},
             memory_pressure: 0,
             memory_used: "",
+            memory_cached: "",
+            memory_free: "",
             memory_total: "",
-            cpu_analytic: [],
+            cpu_analytic: {
+                datasets: [{
+                    label: 'cpu user',
+                    borderColor: '#36a3eb',
+                    backgroundColor: '#36a3eb',
+                    pointRadius: 0,
+                    data: [],
+                }, {
+                    label: 'cpu total',
+                    borderColor: '#ff6385',
+                    backgroundColor: '#ff6385',
+                    pointRadius: 0,
+                    data: [],
+                }],
+                options: {
+                    responsive: true,
+                    maintainAspectRatio:false,
+                    responsiveAnimationDuration: 0, // 调整大小后的动画持续时间
+                    elements: {
+                        line: {
+                            tension: 0 // 禁用贝塞尔曲线
+                        }
+                    },
+                    scales: {
+                        yAxes: [{
+                            ticks: {
+                                max: 100,
+                                min: 0,
+                                stepSize: 20,
+                                display: true
+                            }
+                        }],
+                        xAxes: [
+                            {
+                                type: "time",
+                                time: {
+                                    unit: 'minute',
+                                }
+                            }
+                        ]
+                    }
+                },
+            },
             cpu: 0,
             disk_percentage: 0,
             disk_total: "",
             disk_used: "",
+            uptime: "",
+            loadavg: {}
         }
     },
     created() {
@@ -87,17 +133,32 @@ export default {
             const r = JSON.parse(m.data)
             console.log(r)
             this.cpu = r.cpu_system + r.cpu_user
-            this.cpu_analytic.push({x: new Date(), y: this.cpu})
-            if (this.cpu_analytic.length > 30) {
-                this.cpu_analytic.shift()
+            const time = new Date()
+            //this.cpu_analytic.labels.push(time)
+            this.cpu_analytic.datasets[0].data
+                .push({x: time, y: r.cpu_user})
+            this.cpu_analytic.datasets[1].data
+                .push({x: time, y: this.cpu})
+            if (this.cpu_analytic.datasets[0].data.length > 30) {
+                this.cpu_analytic.datasets[0].data.shift()
+                this.cpu_analytic.datasets[1].data.shift()
             }
             this.cpu = this.cpu.toFixed(2)
             this.memory_pressure = r.memory_pressure
             this.memory_used = r.memory_used
+            this.memory_cached = r.memory_cached
+            this.memory_free = r.memory_free
             this.memory_total = r.memory_total
             this.disk_percentage = r.disk_percentage
             this.disk_used = r.disk_used
             this.disk_total = r.disk_total
+            let uptime = Math.floor(r.uptime)
+            let uptime_days = Math.floor(uptime / 86400)
+            uptime -= uptime_days * 86400
+            let uptime_hours = Math.floor(uptime / 3600)
+            uptime -= uptime_hours * 3600
+            this.uptime = uptime_days + 'd ' + uptime_hours + 'h ' +  Math.floor(uptime/60) + 'm'
+            this.loadavg = r.loadavg
         }
     }
 }
@@ -110,20 +171,16 @@ export default {
         margin: 10px 0;
     }
 
-    .chart-card-content, .chart-wrapper, .chart {
-        overflow: hidden;
-    }
-}
-
-.row-two {
-    .ant-card-body {
-        min-height: 255px;
+    .chart {
+        max-height: 300px;
     }
-}
 
-.row-three {
-    .ant-card {
-        min-height: 377px;
+    .chart_dashboard {
+        padding: 50px;
+        .description {
+            width: 120px;
+            text-align: center
+        }
     }
 }
 </style>

+ 1 - 1
nginx-ui-frontend/vue.config.js

@@ -18,7 +18,7 @@ module.exports = {
         },
     },
     devServer: {
-        proxy: 'http://localhost:9000'
+        proxy: 'https://nginx.jackyu.cn/'
     },
 
     productionSourceMap: false,

+ 44 - 2
nginx-ui-frontend/yarn.lock

@@ -1069,6 +1069,13 @@
     "@types/connect" "*"
     "@types/node" "*"
 
+"@types/chart.js@^2.7.55":
+  version "2.9.31"
+  resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.31.tgz#e8ebc7ed18eb0e5114c69bd46ef8e0037c89d39d"
+  integrity sha512-hzS6phN/kx3jClk3iYqEHNnYIRSi4RZrIGJ8CDLjgatpHoftCezvC44uqB3o3OUm9ftU1m7sHG8+RLyPTlACrA==
+  dependencies:
+    moment "^2.10.2"
+
 "@types/connect-history-api-fallback@*":
   version "1.3.3"
   resolved "https://registry.npm.taobao.org/@types/connect-history-api-fallback/download/@types/connect-history-api-fallback-1.3.3.tgz#4772b79b8b53185f0f4c9deab09236baf76ee3b4"
@@ -2571,6 +2578,29 @@ chardet@^0.7.0:
   resolved "https://registry.npm.taobao.org/chardet/download/chardet-0.7.0.tgz?cache=0&sync_timestamp=1601032454247&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchardet%2Fdownload%2Fchardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
   integrity sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=
 
+chart.js@^2.9.4:
+  version "2.9.4"
+  resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"
+  integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
+  dependencies:
+    chartjs-color "^2.1.0"
+    moment "^2.10.2"
+
+chartjs-color-string@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"
+  integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
+  dependencies:
+    color-name "^1.0.0"
+
+chartjs-color@^2.1.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0"
+  integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==
+  dependencies:
+    chartjs-color-string "^0.6.0"
+    color-convert "^1.9.3"
+
 check-types@^8.0.3:
   version "8.0.3"
   resolved "https://registry.npm.taobao.org/check-types/download/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
@@ -2770,7 +2800,7 @@ collection-visit@^1.0.0:
     map-visit "^1.0.0"
     object-visit "^1.0.0"
 
-color-convert@^1.9.0, color-convert@^1.9.1:
+color-convert@^1.9.0, color-convert@^1.9.1, color-convert@^1.9.3:
   version "1.9.3"
   resolved "https://registry.npm.taobao.org/color-convert/download/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
   integrity sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=
@@ -6021,7 +6051,12 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.5"
 
-moment@^2.21.0, moment@^2.24.0:
+moment-duration-format@^2.3.2:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/moment-duration-format/-/moment-duration-format-2.3.2.tgz#5fa2b19b941b8d277122ff3f87a12895ec0d6212"
+  integrity sha512-cBMXjSW+fjOb4tyaVHuaVE/A5TqkukDWiOfxxAjY+PEqmmBQlLwn+8OzwPiG3brouXKY5Un4pBjAeB6UToXHaQ==
+
+moment@^2.10.2, moment@^2.21.0, moment@^2.24.0:
   version "2.29.1"
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
   integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
@@ -8871,6 +8906,13 @@ vm-browserify@^1.0.1:
   resolved "https://registry.npm.taobao.org/vm-browserify/download/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
   integrity sha1-eGQcSIuObKkadfUR56OzKobl3aA=
 
+vue-chartjs@^3.5.1:
+  version "3.5.1"
+  resolved "https://registry.yarnpkg.com/vue-chartjs/-/vue-chartjs-3.5.1.tgz#d25e845708f7744ae51bed9d23a975f5f8fc6529"
+  integrity sha512-foocQbJ7FtveICxb4EV5QuVpo6d8CmZFmAopBppDIGKY+esJV8IJgwmEW0RexQhxqXaL/E1xNURsgFFYyKzS/g==
+  dependencies:
+    "@types/chart.js" "^2.7.55"
+
 vue-cli-plugin-generate-build-id@0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/vue-cli-plugin-generate-build-id/-/vue-cli-plugin-generate-build-id-0.1.0.tgz#164108d3e7ba72041965dbd530c2c30efb6ce09f"

+ 6 - 0
server/api/analytic.go

@@ -9,6 +9,8 @@ import (
     "github.com/gorilla/websocket"
     "github.com/mackerelio/go-osstat/cpu"
     "github.com/mackerelio/go-osstat/memory"
+    "github.com/mackerelio/go-osstat/uptime"
+    "github.com/mackerelio/go-osstat/loadavg"
     "net/http"
     "strconv"
     "time"
@@ -72,6 +74,10 @@ func Analytic(c *gin.Context) {
             response["cpu_idle"], _ = strconv.ParseFloat(fmt.Sprintf("%.2f",
                 float64(after.Idle-before.Idle)/total*100), 64)
 
+            response["uptime"], _ = uptime.Get()
+            response["uptime"] = response["uptime"].(time.Duration) / time.Second
+            response["loadavg"], _ = loadavg.Get()
+
             used, _total, percentage, err := tool.DiskUsage(".")
 
             response["disk_used"] = used