瀏覽代碼

增加备份计划、备份日志模块

chenwen 1 年之前
父節點
當前提交
6018aa06b5
共有 4 個文件被更改,包括 140 次插入15 次删除
  1. 17 0
      src/api/dbBackup.js
  2. 56 9
      src/pages/sys/DbBackupLog.vue
  3. 23 6
      src/pages/sys/DbBackupSchedule.vue
  4. 44 0
      src/utils/dwnloadRequest.js

+ 17 - 0
src/api/dbBackup.js

@@ -1,4 +1,5 @@
 import request from '../utils/request';
+import dwnloadRequest from '../utils/dwnloadRequest';
 
 const api={}
 
@@ -29,4 +30,20 @@ api.getBackupTarget=()=>{
 	});
 }
 
+api.manualBackup=()=>{
+	return request({
+	    url: '/backup/manual/glb',
+	    method: 'get'
+	});
+}
+
+api.downloadBackup=(backupId)=>{
+	return dwnloadRequest({
+	    url: '/backup/download',
+	    method: 'post',
+		responseType:'blob',
+		data: {backupId,_num:Math.random()}
+	});
+}
+
 export  default api

+ 56 - 9
src/pages/sys/DbBackupLog.vue

@@ -7,7 +7,7 @@
 			</el-select>
 		  </el-form-item>
 		  
-		  <el-form-item label="操作时间">
+		  <el-form-item label="备份时间">
 		    <el-date-picker
 		            v-model="queryForm.dataTime"
 		            type="daterange"
@@ -34,12 +34,22 @@
 			  
 				<template #tabColumns={indexGenerate}>
 					  <el-table-column type="index" :index="indexGenerate" label="序号" width="60" align="center"/>
-					  <el-table-column prop="deviceName" label="备份任务" width="160" />
-					  <el-table-column prop="startTIme" label="开始时间" width="160" />
+					  <el-table-column prop="taskName" label="备份任务" width="160" />
+					  <el-table-column prop="startTime" label="开始时间" width="160" />
 					  <el-table-column prop="endTime" label="结束时间" width="160" />
-					  <el-table-column prop="operator" label="备份人" width="120" />
-					  <el-table-column prop="status" label="状态" width="120" />
-					  <el-table-column prop="note" label="备注" width="120" />
+					  <el-table-column prop="duration" label="用时(秒)" width="100" />
+					  <el-table-column prop="operator" label="操作人" width="120"/>
+					  <el-table-column prop="status" label="状态" width="90">
+						  <template #default="scope">
+							  {{ {'success':'成功','running':'备份中','failed':'已失败'}[scope.row.status] || '' }}
+						  </template>
+					  </el-table-column>
+					  <el-table-column prop="note" label="备份文件" width="100" align="center">
+						  <template #default="scope">
+							  <el-button type="primary"  plain  size="small" v-if="scope.row.status=='success'" @click="dwnbackup(scope.row)">下载</el-button>
+						  </template>
+						  
+					  </el-table-column>
 					  
 				</template>
 				
@@ -108,14 +118,51 @@
 				sortidx[data.tagCode]=true
 			}
 		})
-		let defaultBack={sort:'glb',name:'全表结构'}
+		let defaultBack={sort:'glb',name:'全表结构'}
 		return [defaultBack,...sortary]
 	}
 	
 
+	const dwnbackup=(record)=>{
+		api.downloadBackup(record.backupId).then(resp=>{
+			//console.log(resp)
+			
+			const fileReader = new FileReader();
+			fileReader.readAsText(resp.data)
+			fileReader.onload = (event) => {
+				try{
+					const data = JSON.parse(event.target.result)
+					if(data.code!=0){
+						ElMessage.error(data.msg || '下载文件失败')
+					}
+				}
+				catch(err){
+					parseDownloadFile(resp)
+				}
+			}
+		}).catch(err=>{
+			console.log(err)
+			ElMessage.error('下载文件出现网络错误')
+		})
+	}
 	
-	
-	
+	const parseDownloadFile=(res)=>{
+		const fileName = decodeURI(res.headers['content-disposition']).replace(/\w+;filename=(.*)/, '$1')
+		//console.log(fileName)
+		const blob = new Blob([res.data], { type: "application/octet-stream"})
+		const fileUrl = window.URL.createObjectURL(blob)
+		
+		let link = document.createElement('a') 
+		link.href = fileUrl 
+		link.download = decodeURI(fileName) //设置下载的文件名
+		link.style.display = 'none'  
+		document.body.appendChild(link) 
+		link.click() 
+		 
+		document.body.removeChild(link)
+		window.URL.revokeObjectURL(fileUrl)
+		
+	}
 	
 	
 	

+ 23 - 6
src/pages/sys/DbBackupSchedule.vue

@@ -34,9 +34,9 @@
 					<el-form-item label="备份时间">
 						<el-time-picker v-model="formModel.backupTime" placeholder="选择备份时点"  format="HH:mm" value-format="HH:mm"/>
 					</el-form-item>
-					<el-form-item label="状态">
+					<!-- <el-form-item label="状态">
 						<el-switch v-model="formModel.disableIf" active-text="启用" inactive-text="禁用" :active-value="false" :inactive-value="true"/>
-					</el-form-item>
+					</el-form-item> -->
 					<el-form-item label=" ">
 						<el-button type="primary" @click="save">应用</el-button>
 					</el-form-item>
@@ -50,7 +50,7 @@
 						表结构
 					</el-form-item>
 					<el-form-item label=" ">
-						<el-button type="primary">开始手动备份</el-button>
+						<el-button type="primary" @click="manualBackup">开始手动备份</el-button>
 					</el-form-item>
 				</div>	
 				
@@ -105,7 +105,7 @@
 			}
 			sortary[sortidx[data.tagCode]]['tabs'].push(data)
 		})
-		let defaultBack={sort:'glb',name:'全部表结构',tabs:[{itemName:'全部表结构',itemCode:'all table'}]}
+		let defaultBack={sort:'glb',name:'全库表结构',tabs:[{itemName:'全库表结构',itemCode:'all table'}]}
 		return [defaultBack,...sortary]
 	}
 	
@@ -119,7 +119,7 @@
 		//console.log(selsort[0])
 		let {sort:targetSort,name:sortName}=selsort[0]
 		let backupMode=targetSort=='gather'?'increase':'full'
-		let backupCon=targetSort=='glb'?'onlyCreate':'onlyData'
+		let backupCon=targetSort=='glb'?'only_structure':'only_data'
 		Object.assign(formModel,{targetSort,sortName,backupMode,backupCon,disableIf:false,backupTime:null})
 		
 		load(targetSort)
@@ -144,7 +144,10 @@
 	}
 	
 	const save=()=>{
-		api.save(toRaw(formModel)).then(resp=>{
+		let saveData=Object.assign({},formModel)
+		console.log(saveData)
+		saveData['scheduleName']=saveData['sortName']+'备份'
+		api.save(saveData).then(resp=>{
 			if(resp.code==0){
 				ElMessage.success('操作成功')
 				load(formModel.targetSort)
@@ -157,6 +160,20 @@
 			ElMessage.error('操作出错')
 		})
 	}
+	
+	const manualBackup=()=>{
+		api.manualBackup().then(resp=>{
+			if(resp.code!=0){
+				ElMessage.error(resp.msg || '操作失败')
+			}
+			else{
+				ElMessage.success('备份操作已提交,请在备份日志中查看结果')
+			}
+		}).catch(err=>{
+			console.log(err)
+			ElMessage.error('操作出错')
+		})
+	}
 </script>
 
 <style scoped>

+ 44 - 0
src/utils/dwnloadRequest.js

@@ -0,0 +1,44 @@
+import axios from 'axios';
+import app from './app.js'
+import { useHomeStore } from "../store/home.js"
+
+const store=useHomeStore()
+
+axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
+
+//axios  默认发送数据采用 application/json,传参用params,实际用的是get请求参数附加到url后,用data传参,参数放在request boy中
+const service = axios.create({
+    baseURL:import.meta.env.VITE_API_PATH,    //http://127.0.0.1:8080/zl  //http://42.56.120.92:9602/zl-opd-server
+    timeout: 15000
+});
+
+service.interceptors.request.use(
+    config => {
+		config.headers.token=app.getToken()
+		return config;
+    },
+    error => {
+        return Promise.reject();
+    }
+);
+
+service.interceptors.response.use(
+    response => {
+		if (response.status === 200) {
+			if(response.data.code==401){
+				app.clearToken()  //清除无效登录信息才能进入登录页面
+				store.connStatus=401
+				return Promise.reject('未登录或登录认证已过期');
+			}
+			return response;
+        } else {
+			store.connStatus=404
+            Promise.reject('远程服务出错');
+        }
+    },
+    error => {
+        return Promise.reject('请求出现网络问题');
+    }
+);
+
+export default service;