Browse Source

数据库备份

master
xuhuajiao 9 months ago
parent
commit
0c199ba0bb
  1. 39
      src/api/system/sql.js
  2. 13
      src/utils/index.js
  3. 157
      src/views/system/database/index.vue

39
src/api/system/sql.js

@ -0,0 +1,39 @@
import request from '@/utils/request'
// 备份数据库
export function add(data) {
return request({
url: 'api/database/backupSQL',
method: 'post',
data
})
}
// 获取备份数据库文件名
export function FetchBackupName(params) {
return request({
url: 'api/database/getBackupName',
method: 'get',
params
})
}
// 删除数据库备份
export function del(data) {
return request({
url: 'api/database/deleteDatabaseBackup',
method: 'post',
data
})
}
// 还原数据库
export function FetchrestoreDatabase(params) {
return request({
url: 'api/database/restoreDatabase',
method: 'get',
params
})
}
export default { add, FetchBackupName, del, FetchrestoreDatabase }

13
src/utils/index.js

@ -387,6 +387,19 @@ export function downloadFile(obj, name, suffix) {
document.body.removeChild(link) document.body.removeChild(link)
} }
// 下载文件
export function downloadDataBase(obj, name, suffix) {
const url = window.URL.createObjectURL(new Blob([obj]))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
const fileName = name + '.' + suffix
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
// new - 导出 // new - 导出
export function exportFile(url, fileName) { export function exportFile(url, fileName) {
const link = document.createElement('a') const link = document.createElement('a')

157
src/views/system/database/index.vue

@ -39,20 +39,22 @@
v-loading="crud.loading" v-loading="crud.loading"
:data="crud.data" :data="crud.data"
style="width: 100%;" style="width: 100%;"
height="calc(100vh - 380px)"
@selection-change="crud.selectionChangeHandler" @selection-change="crud.selectionChangeHandler"
> >
<el-table-column type="selection" align="center" width="55" /> <el-table-column type="selection" align="center" width="55" />
<el-table-column prop="name" :show-overflow-tooltip="true" label="备份文件名" min-width="240" />
<el-table-column prop="remark" :show-overflow-tooltip="true" label="备注" min-width="200" />
<el-table-column prop="file_size" label="大小" min-width="85">
<!-- <template slot-scope="scope">
{{ getFileSize(scope.row.file_size) }}
</template> -->
<el-table-column prop="filename" :show-overflow-tooltip="true" label="备份文件名" min-width="240" />
<el-table-column prop="remarks" :show-overflow-tooltip="true" label="备注" min-width="200" />
<el-table-column prop="filesize" label="大小" min-width="85">
<template slot-scope="scope">
<div v-if="scope.row.filesize!==0">{{ getFileSize(scope.row.filesize) }}</div>
<div v-else>-</div>
</template>
</el-table-column> </el-table-column>
<el-table-column prop="createBy" label="操作人" align="center" width="100" />
<el-table-column prop="createTime" label="操作时间" align="center" width="180">
<el-table-column prop="create_by" label="操作人" align="center" width="100" />
<el-table-column prop="create_time" label="操作时间" align="center" width="180">
<template slot-scope="scope"> <template slot-scope="scope">
<div>{{ scope.row.createTime | parseTime }}</div>
<div>{{ scope.row.create_time | parseTime }}</div>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -66,34 +68,34 @@
<p class="tipMsg">这里为技术人员维护系统时使用普通用户无需设置</p> <p class="tipMsg">这里为技术人员维护系统时使用普通用户无需设置</p>
<p class="delt-tip"><span>注意强行修改会导致系统数据异常或丢失如因用户强行修改本系统不负责因此导致的相关后果</span></p> <p class="delt-tip"><span>注意强行修改会导致系统数据异常或丢失如因用户强行修改本系统不负责因此导致的相关后果</span></p>
</div> </div>
<el-form :model="form" style="margin-top:30px;" @submit.native.prevent>
<el-form ref="verfiyForm" :model="verfiyForm" style="margin-top:30px;" @submit.native.prevent>
<el-form-item label="维护验证码" label-width="110px"> <el-form-item label="维护验证码" label-width="110px">
<el-input v-model="form.verifyCode" show-password style="width: 480px;" />
<el-input v-model="verfiyForm.verifyCode" show-password style="width: 480px;" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="verifyDialogVisible=false">取消 </el-button>
<el-button @click="handleClose">取消 </el-button>
<el-button type="primary" @click.native="handleConfirm">确定</el-button> <el-button type="primary" @click.native="handleConfirm">确定</el-button>
</div> </div>
</div> </div>
</el-dialog> </el-dialog>
<!-- form --> <!-- form -->
<el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title">
<el-dialog append-to-body :close-on-click-modal="false" :modal-append-to-body="false" :before-close="crud.cancelCU" :visible="formVisible" :title="crud.status.title">
<span class="dialog-right-top" /> <span class="dialog-right-top" />
<span class="dialog-left-bottom" /> <span class="dialog-left-bottom" />
<div class="setting-dialog"> <div class="setting-dialog">
<el-form ref="form" :rules="rules" :model="form" size="small" label-width="100px"> <el-form ref="form" :rules="rules" :model="form" size="small" label-width="100px">
<el-form-item label="备份名称" prop="name">
<el-input v-model="form.name" style="width: 580px;" />
<el-form-item label="备份名称" prop="filename">
<el-input v-model="form.filename" style="width: 580px;" disabled />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="4" style="width: 580px;" />
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" :rows="4" style="width: 580px;" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">保存</el-button>
<el-button type="text" @click="handleClose">取消</el-button>
<el-button :loading="crud.status.cu === 2" type="primary" @click="toAdd">保存</el-button>
</div> </div>
</div> </div>
</el-dialog> </el-dialog>
@ -103,17 +105,18 @@
<script> <script>
import { verifyMaintenance } from '@/api/system/field' import { verifyMaintenance } from '@/api/system/field'
import { encrypt } from '@/utils/rsaEncrypt' import { encrypt } from '@/utils/rsaEncrypt'
import crudNotify from '@/api/system/notify'
// import { FetchBackupSQL } from '@/api/system/sql'
import crudSql from '@/api/system/sql'
import CRUD, { presenter, header, form, crud } from '@crud/crud' import CRUD, { presenter, header, form, crud } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation' import crudOperation from '@crud/CRUD.operation'
import rrOperation from '@crud/RR.operation' import rrOperation from '@crud/RR.operation'
import DateRangePicker from '@/components/DateRangePicker' import DateRangePicker from '@/components/DateRangePicker'
import pagination from '@crud/Pagination' import pagination from '@crud/Pagination'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
// import { exportFile } from '@/utils/index'
import { downloadDataBase } from '@/utils/index'
// import qs from 'qs' // import qs from 'qs'
const defaultForm = { id: null, name: '', remark: null }
const defaultForm = { id: null, filename: '', remarks: null }
export default { export default {
name: 'Database', name: 'Database',
@ -121,8 +124,8 @@ export default {
cruds() { cruds() {
return CRUD({ return CRUD({
title: '数据备份', title: '数据备份',
url: 'api/notice/initNotice',
crudMethod: { ...crudNotify },
url: 'api/database/databaseList',
crudMethod: { ...crudSql },
sort: [], sort: [],
optShow: { optShow: {
add: true, add: true,
@ -143,14 +146,15 @@ export default {
del: ['admin', 'database:del'] del: ['admin', 'database:del']
}, },
verifyDialogVisible: false, verifyDialogVisible: false,
form: {
verfiyForm: {
verifyCode: '' verifyCode: ''
}, },
btn: '', btn: '',
showVerifyDialog: true, showVerifyDialog: true,
formVisible: false,
blurryTime: null, blurryTime: null,
rules: { rules: {
name: [
filename: [
{ required: true, message: '备份名称不可为空', trigger: 'blur' } { required: true, message: '备份名称不可为空', trigger: 'blur' }
] ]
} }
@ -173,9 +177,10 @@ export default {
} }
}, },
getFileSize(fileSize) { getFileSize(fileSize) {
const fileSizeInMB = (fileSize / (1024 * 1024)).toFixed(2) + 'MB'
const fileSizeInKB = (fileSize / 1024).toFixed(2) + 'kB' const fileSizeInKB = (fileSize / 1024).toFixed(2) + 'kB'
const fileSizeInB = fileSize + 'B'
return (fileSize / 1024) <= 0.01 ? fileSizeInB : fileSizeInKB
return (fileSize / (1024 * 1024)) < 0.01 ? fileSizeInKB : fileSizeInMB
}, },
[CRUD.HOOK.beforeRefresh]() { [CRUD.HOOK.beforeRefresh]() {
if (this.blurryTime) { if (this.blurryTime) {
@ -207,14 +212,19 @@ export default {
} }
}, },
handleConfirm() { handleConfirm() {
verifyMaintenance(encrypt(this.form.verifyCode)).then((res) => {
verifyMaintenance(encrypt(this.verfiyForm.verifyCode)).then((res) => {
if (res) { if (res) {
// //
this.verifyDialogVisible = false this.verifyDialogVisible = false
this.form.verifyCode = ''
this.verfiyForm.verifyCode = ''
this.showVerifyDialog = false this.showVerifyDialog = false
if (this.btn === 'add') { if (this.btn === 'add') {
this.crud.toAdd()
crudSql.FetchBackupName().then((data) => {
this.crud.form.filename = data
this.formVisible = true
}).catch(err => {
console.log(err)
})
} else if (this.btn === 'edit') { } else if (this.btn === 'edit') {
this.crud.toEdit(this.crud.selections[0]) this.crud.toEdit(this.crud.selections[0])
} else if (this.btn === 'del') { } else if (this.btn === 'del') {
@ -230,9 +240,31 @@ export default {
} }
}) })
}, },
handleClose(done) {
this.form.verifyCode = ''
done()
toAdd() {
crudSql.add(this.crud.form).then((res) => {
console.log(res)
if (res.code !== 500) {
this.$message({ message: '新增成功', type: 'success', offset: 8 })
} else {
this.$message({ message: res.message, type: 'error', offset: 8 })
}
this.handleClose()
this.crud.refresh()
}).catch(err => {
console.log(err)
})
},
handleClose() {
if (this.$refs.verfiyForm) {
this.verfiyForm.verifyCode = ''
this.$refs.verfiyForm.resetFields()
this.verifyDialogVisible = false
}
if (this.$refs.form) {
this.crud.form.filename = null
this.crud.form.remarks = null
this.formVisible = false
}
}, },
toDelete(data) { toDelete(data) {
this.$confirm('此操作将删除当前所选数据' + '<span>你是否还要继续?</span>', '提示', { this.$confirm('此操作将删除当前所选数据' + '<span>你是否还要继续?</span>', '提示', {
@ -241,22 +273,20 @@ export default {
type: 'warning', type: 'warning',
dangerouslyUseHTMLString: true dangerouslyUseHTMLString: true
}).then(() => { }).then(() => {
// const ids = data.map(item => item.id)
// const params = {
// 'ids': ids,
// 'operator': this.user.username
// }
// crudEditing.del(params).then((res) => {
// console.log(res)
// if (res.code !== 500) {
// this.$message({ message: '', type: 'success', offset: 8 })
// } else {
// this.$message({ message: res.message, type: 'error', offset: 8 })
// }
// this.initData()
// }).catch(err => {
// console.log(err)
// })
const params = data.map(item => {
return item.id
})
crudSql.del(params).then((res) => {
console.log(res)
if (res.code !== 500) {
this.$message({ message: '删除成功', type: 'success', offset: 8 })
} else {
this.$message({ message: res.message, type: 'error', offset: 8 })
}
this.crud.refresh()
}).catch(err => {
console.log(err)
})
}).catch(() => { }).catch(() => {
}) })
}, },
@ -269,10 +299,12 @@ export default {
type: 'warning', type: 'warning',
dangerouslyUseHTMLString: true dangerouslyUseHTMLString: true
}).then(() => { }).then(() => {
// const params = {
// 'isType': this.isType
// }
// exportFile(this.baseApi + '/api/categoryField/download?' + qs.stringify(params, { indices: false }))
const url = this.baseApi + '/downloadFile' + data.filepath
fetch(url).then(res => res.blob()).then(blob => {
downloadDataBase(blob, data.filename.split('.')[0], 'sql')
}).catch(() => {
this.$message({ message: '下载文件失败!', type: 'error', offset: 8 })
})
this.crud.downloadLoading = false this.crud.downloadLoading = false
}).catch(() => { }).catch(() => {
}) })
@ -285,10 +317,19 @@ export default {
type: 'warning', type: 'warning',
dangerouslyUseHTMLString: true dangerouslyUseHTMLString: true
}).then(() => { }).then(() => {
// const params = {
// 'isType': this.isType
// }
// exportFile(this.baseApi + '/api/categoryField/download?' + qs.stringify(params, { indices: false }))
const params = {
'id': data.id
}
crudSql.FetchrestoreDatabase(params).then((res) => {
if (res.code !== 500) {
this.$message({ message: res, type: 'success', offset: 8 })
} else {
this.$message({ message: res.message, type: 'error', offset: 8 })
}
this.crud.refresh()
}).catch(err => {
console.log(err)
})
}).catch(() => { }).catch(() => {
}) })
} }

Loading…
Cancel
Save