Browse Source

数据库备份功能

wenningning 1 year ago
parent
commit
225b49ad8b
3 changed files with 300 additions and 0 deletions
  1. 40 0
      src/api/mysql_backups.js
  2. 13 0
      src/router/index.js
  3. 247 0
      src/views/mysql-backups/index.vue

+ 40 - 0
src/api/mysql_backups.js

@@ -0,0 +1,40 @@
+import request from '@/utils/request'
+
+export function getList(params) {
+  return request({
+    url: '/mgr/mysql_backups/page',
+    method: 'POST',
+    loading: true,
+    data: params,
+    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+  })
+}
+
+/** 数据备份 */
+export function mysqlBackups(params) {
+  return request({
+    url: '/mgr/mysql_backups',
+    method: 'POST',
+    loading: true,
+    data: params
+  })
+}
+
+/** 删除数据备份 */
+export function remove(params) {
+  const ids = params.toString()
+  return request({
+    url: `/mgr/mysql_backups/${ids}`,
+    method: 'DELETE',
+    loading: true,
+    data: params
+  })
+}
+
+/** 数据还原 */
+export function rollback(id) {
+  return request({
+    url: `/mgr/mysql_backups/${id}`,
+    method: 'put'
+  })
+}

+ 13 - 0
src/router/index.js

@@ -791,6 +791,19 @@ export const adminRoutes = [
     ]
   },
   {
+    path: '/mysql_backups',
+    component: Layout,
+    redirect: '/mysql_backups/index',
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/mysql-backups/index'),
+        name: 'mysql-backups-index',
+        meta: { title: '数据备份', icon: 'el-icon-refresh', noCache: true }
+      }
+    ]
+  },
+  {
     path: '/device_frame',
     component: Layout,
     redirect: '/device_frame/index',

+ 247 - 0
src/views/mysql-backups/index.vue

@@ -0,0 +1,247 @@
+<template>
+  <div>
+    <div style="margin: 10px">
+      <el-select v-model="databaseName" @change="changeSearch" filterable allow-create default-first-option clearable>
+        <el-option v-for="(item, index) in databaseOptions" :key="index" :label="item" :value="item" />
+      </el-select>
+      <el-button style="margin-left: 20px" size="mini" type="primary" @click="addLog">新增备份</el-button>
+      <el-button style="margin-left: 20px" size="mini" type="" @click="clickSearch">刷新</el-button>
+      <span style="color: red; font-size: 12px;margin-left: 20px">注意:1、该功能只能在正式环境中使用,其他环境会出现问题;2、存储地址默认是/opt/v70/api/upload,因为映射的关系,在物理机上/mnt/ncs/upload可以找到备份文件;3、因为异步的关系,操作完成需要手动刷新数据。</span>
+    </div>
+    <div class="table-wrapper" style="padding-bottom: 10px;padding-left: 10px">
+      <el-table :data="tableData.data" id="myTableId" stripe border style="width: 100%" ref="refTable" height="520">
+<!--        <el-table-column type="selection" width="55"></el-table-column>-->
+        <el-table-column prop="database_name" min-width="100" label="数据库" align="center" />
+<!--        <el-table-column prop="container_name" label="容器名称" min-width="100" align="center" />-->
+        <el-table-column prop="backups_path" label="备份地址" min-width="130" align="center" />
+        <el-table-column prop="backups_name" label="文件名" min-width="130" align="center" />
+        <el-table-column prop="operation" label="操作次数" width="100" align="center" />
+        <el-table-column prop="recovery_time" label="恢复时间" width="170" align="center" :formatter="fortDate"/>
+        <el-table-column prop="create_time" label="备份时间" width="170" align="center" :formatter="fortDate" />
+        <el-table-column prop="state" label="状态" width="100" align="center">
+          <template slot-scope="scope">
+            <el-tag v-if="scope.row.status === 0" type="success" disable-transitions>正常</el-tag>
+            <el-tag v-if="scope.row.status === 1" type="danger" disable-transitions>文件不存在</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column style="text-align: left;" label="操作" width="120" fixed="right">
+          <template slot-scope="scope">
+            <el-button v-if="scope.row.status === 0" type="success" size="mini" @click="handlerEdit(scope.row)">
+              还原
+            </el-button>
+            <el-button v-else type="info" size="mini" @click="handlerDel(scope.row.id)">
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-pagination
+          v-if="tableData"
+          slot="pagination"
+          @size-change="handlePageSizeChange"
+          @current-change="handlePageCurrentChange"
+          :current-page="tableData.page_no"
+          :page-sizes="[20, 50, 100, 500]"
+          :page-size="tableData.page_size"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="tableData.data_total">
+      </el-pagination>
+    </div>
+    <el-dialog title="新增备份" :visible.sync="fromDialog" width="700px" :close-on-click-modal="false" :close-on-press-escape="false">
+      <el-form :model="myForm" ref="myForm" :rules="formRules" label-width="140px">
+        <el-form-item label="数据库" prop="database_name">
+          <el-select v-model="myForm.database_name" filterable allow-create default-first-option placeholder="请选择或输入数据库">
+            <el-option v-for="(item, index) in databaseOptions" :key="index" :label="item" :value="item"></el-option>
+          </el-select>
+        </el-form-item>
+<!--        <el-form-item label="是否装在docker">-->
+<!--          <el-radio-group v-model="myForm.bool_docker">-->
+<!--            <el-radio :label="false">没有</el-radio>-->
+<!--            <el-radio :label="true">装在</el-radio>-->
+<!--          </el-radio-group>-->
+<!--        </el-form-item>-->
+<!--        <el-form-item v-if="myForm.bool_docker" label="容器名称" prop="container_name">-->
+<!--          <el-input v-model="myForm.container_name" placeholder="默认mysql" />-->
+<!--        </el-form-item>-->
+        <el-form-item label="备份地址" prop="backups_path">
+          <el-input v-model="myForm.backups_path" placeholder="请输入备份地址" clearable />
+        </el-form-item>
+<!--        <el-form-item label="mysql服务器地址">-->
+<!--          <el-input v-model="myForm.mysql_ip" placeholder="mysql服务器地址" />-->
+<!--          <el-link type="primary" @click="myForm.mysql_ip = serverIP">填入服务器地址</el-link>-->
+<!--        </el-form-item>-->
+
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="fromDialog = false">取 消</el-button>
+        <el-button type="primary" @click="handleConfirm">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { unix2Date } from '@/utils/Foundation'
+import * as API_MysqlBackups from '@/api/mysql_backups'
+export default {
+  name: 'index',
+  data() {
+    return {
+      tableData: [],
+      /** 列表参数 */
+      params: {
+        page_size: 20,
+        page_no: 1,
+        sort: 'create_time',
+        dir: 'desc'
+      },
+      /** ag-grid参数 **/
+      pageData: [],
+      loading: false,
+      fromDialog: false,
+      myForm: {
+        database_name: 'wdkl_ncs',
+        container_name: 'mysql',
+        mysql_ip: '',
+        backups_path: '/opt/v70/api/upload',
+        bool_docker: false
+      },
+      formRules: {
+        database_name: [{ required: true, message: '请选择或输入数据库', trigger: 'change' }],
+        container_name: [{ required: true, message: '请输入容器名称', trigger: 'blur' }],
+        backups_path: [{ required: true, message: '请输入文件存储地址', trigger: 'blur' }],
+        mysql_ip: [{ required: true, message: '请输入mysql服务器地址', trigger: 'blur' }]
+      },
+      databaseOptions:['wdkl_ncs', 'wdkl_nfc', 'kea'],
+      databaseName: '',
+      localIP: '',
+      serverIP: ''
+    }
+  },
+  mounted() {
+    this.localIP = window.location.hostname
+    this.serverIP = this.API_GetIP()
+    this.GET_List()
+  },
+  methods: {
+    /** 分页大小发生改变 */
+    handlePageSizeChange(size) {
+      this.params.page_size = size
+      this.GET_List()
+    },
+    /** 分页页数发生改变 */
+    handlePageCurrentChange(page) {
+      this.params.page_no = page
+      this.GET_List()
+    },
+    GET_List() {
+      API_MysqlBackups.getList(this.params).then(res => {
+        this.tableData = res
+      })
+    },
+    /** 点击搜索按钮 **/
+    clickSearch() {
+      this.params.page_no = 1
+      this.GET_List()
+    },
+    /** 点击每页显示数量 **/
+    fortDate(row, column, cellValue, index) {
+      if (!cellValue) return '暂无'
+      return unix2Date(cellValue * 1000)
+    },
+    handlerEdit(row) {
+      console.log('还原....',row)
+      this.$confirm('您确定要恢复还原吗?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        // if (row.mysql_ip === this.API_GetIP()) {
+        const _this = this
+        API_MysqlBackups.rollback(row.id).then(res => {
+          _this.$message.info(res.message)
+          if (!res.success) {
+            _this.GET_List()
+          }
+        })
+        // } else {
+        //   this.$message({
+        //     type: 'warning',
+        //     message: '该备份文件为存储在' + row.mysql_ip + '机器上,其他环境不能还原'
+        //   })
+        // }
+      }).catch(() => {
+        console.log('取消删除')
+      })
+    },
+    /** 单条数据删除处理 */
+    handlerDel(ids) {
+      this.$confirm('删除操作后数据不可复原,您确定要删除此数据?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        API_MysqlBackups.remove(ids).then(
+            response => {
+              this.GET_List()
+              this.$message({
+                type: 'success',
+                message: '删除成功!'
+              })
+            }
+        )
+      }).catch(() => {
+        console.log('取消删除')
+      })
+    },
+    handleConfirm() {
+      const _this = this
+      this.$refs['myForm'].validate((valid, error) => {
+        if (valid) {
+          const params = _this.MixinClone(_this.myForm)
+            // 新增
+          API_MysqlBackups.mysqlBackups(params).then(res => {
+            _this.fromDialog = false
+            _this.$message.info(res.message)
+          })
+        } else {
+          this.$message.error('表单填写有误,请检查!')
+        }
+      })
+    },
+    API_GetIP() {
+      const regex = /\/\/([\d.]+):\d+/
+      const matches = regex.exec(domain.serverUrl)
+      if (matches !== null) {
+        console.log(matches[1]) // 输出192.168.1.199
+        return matches[1]
+      } else {
+        if (domain.serverUrl.indexOf('localhost') > -1) {
+          return 'localhost'
+        }
+        return ''
+      }
+    },
+    changeSearch() {
+      if (this.databaseName) {
+        this.params.fixedCondition = " database_name='" +this.databaseName + "'"
+      } else {
+        this.params.fixedCondition = ' 1=1 '
+      }
+      this.params.page_no = 1
+      this.GET_List()
+    },
+    addLog() {
+      // if (this.localIP !== this.serverIP) {
+      //   this.$message.error('浏览器地址与请求服务器地址不一致!')
+      //   return
+      // }
+      this.fromDialog = true
+    },
+  }
+}
+</script>
+
+<style scoped>
+
+</style>