feat: 数据库备份功能优化

This commit is contained in:
许俊杰 2025-03-20 10:19:11 +08:00
parent 8c108fe80b
commit 91091df964
14 changed files with 319 additions and 79 deletions

View File

@ -758,7 +758,7 @@ public enum ErrorCodeEnum
db1001,
/// <summary>
/// 数据表不存在
/// 不允许添加相同字段名
/// </summary>
[ErrorCodeItemMetadata("不允许添加相同字段名")]
db1002,
@ -768,6 +768,12 @@ public enum ErrorCodeEnum
/// </summary>
[ErrorCodeItemMetadata("实体文件不存在或匹配不到。如果是刚刚生成的实体,请重启服务后再试")]
db1003,
/// <summary>
/// 数据库类型:{0} 不支持备份
/// </summary>
[ErrorCodeItemMetadata("数据库类型:{0} 不支持备份")]
db1004,
/// <summary>
/// 父节点不存在

View File

@ -0,0 +1,14 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
public class DbOutput
{
public string ConfigId { get; set; }
public string DbName { get; set; }
}

View File

@ -33,9 +33,21 @@ public class SysDatabaseService : IDynamicApiController, ITransient
/// </summary>
/// <returns></returns>
[DisplayName("获取库列表")]
public List<string> GetList()
public List<DbOutput> GetList()
{
return App.GetOptions<DbConnectionOptions>().ConnectionConfigs.Select(u => u.ConfigId.ToString()).ToList();
var dbOutputs = new List<DbOutput>();
var configIds = App.GetOptions<DbConnectionOptions>().ConnectionConfigs.Select(u => u.ConfigId.ToString()).ToList();
foreach (var config in configIds)
{
var db = _db.AsTenant().GetConnectionScope(config);
dbOutputs.Add(new DbOutput
{
ConfigId = config,
DbName = db.Ado.Connection.Database
});
}
return dbOutputs;
}
/// <summary>
@ -305,7 +317,9 @@ public class SysDatabaseService : IDynamicApiController, ITransient
{
var config = App.GetOptions<DbConnectionOptions>().ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId);
input.Position = string.IsNullOrWhiteSpace(input.Position) ? "Admin.NET.Application" : input.Position;
input.EntityName = string.IsNullOrWhiteSpace(input.EntityName) ? (config.DbSettings.EnableUnderLine ? CodeGenUtil.CamelColumnName(input.TableName, null) : input.TableName) : input.EntityName;
input.EntityName = string.IsNullOrWhiteSpace(input.EntityName)
? (config.DbSettings.EnableUnderLine ? CodeGenUtil.CamelColumnName(input.TableName, null) : input.TableName)
: input.EntityName;
string[] dbColumnNames = Array.Empty<string>();
// Entity.cs.vm中是允许创建没有基类的实体的所以这里也要做出相同的判断
if (!string.IsNullOrWhiteSpace(input.BaseClassName))
@ -325,7 +339,8 @@ public class SysDatabaseService : IDynamicApiController, ITransient
if (_codeGenOptions.BaseEntityNames.Contains(input.BaseClassName, StringComparer.OrdinalIgnoreCase))
dbColumnInfos = dbColumnInfos.Where(u => !dbColumnNames.Contains(u.PropertyName, StringComparer.OrdinalIgnoreCase)).ToList();
var dbTableInfo = db.DbMaintenance.GetTableInfoList(false).FirstOrDefault(u => u.Name == input.TableName || u.Name == input.TableName.ToLower()) ?? throw Oops.Oh(ErrorCodeEnum.db1001);
var dbTableInfo = db.DbMaintenance.GetTableInfoList(false).FirstOrDefault(u => u.Name == input.TableName || u.Name == input.TableName.ToLower()) ??
throw Oops.Oh(ErrorCodeEnum.db1001);
var templatePath = GetEntityTemplatePath();
var tContent = File.ReadAllText(templatePath);
var tResult = _viewEngine.RunCompileFromCached(tContent, new
@ -365,6 +380,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
entityType = item.Type;
break;
}
if (entityType == null) throw Oops.Oh(ErrorCodeEnum.db1003);
input.EntityName = entityType.Name;
@ -385,7 +401,8 @@ public class SysDatabaseService : IDynamicApiController, ITransient
if (input.FilterExistingData && records.Count != 0)
{
// 获取实体类型-所有种数据数据类型
var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName))
var entityTypes = App.EffectiveTypes.Where(u =>
!u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName))
.Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
.ToList();
if (entityTypes.Count == 1) // 只有一个实体匹配才能过滤
@ -424,6 +441,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
recordsToRemove.Add(record);
}
}
foreach (var itemToRemove in recordsToRemove)
{
records.Remove(itemToRemove);
@ -436,7 +454,8 @@ public class SysDatabaseService : IDynamicApiController, ITransient
// 检查有没有 System.Text.Json.Serialization.JsonIgnore 或 Newtonsoft.Json.JsonIgnore 的属性
// 如果 JsonIgnore 和 SugarColumn 都存在那么后成序更化时就生成这了这些字段就需要在这里另外补充以处理用户表SysUser中的Password为例
var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute<System.Text.Json.Serialization.JsonIgnoreAttribute>() != null ||
p.GetAttribute<Newtonsoft.Json.JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null).ToList();
p.GetAttribute<Newtonsoft.Json.JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null)
.ToList();
var jsonIgnoreInfo = new List<List<JsonIgnoredPropertyData>>();
if (jsonIgnoreProperties.Count > 0)
{
@ -456,8 +475,10 @@ public class SysDatabaseService : IDynamicApiController, ITransient
else if (v.GetType() == typeof(DateTime))
strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")";
}
record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue });
}
recordIndex++;
jsonIgnoreInfo.Add(record);
}
@ -497,6 +518,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
{
value = $"DateTime.Parse(\"{((DateTime)value):yyyy-MM-dd HH:mm:ss.fff}\")";
}
return $"{prop.Name}={value}";
}))).ToList();
@ -556,6 +578,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
{
description = ((DescriptionAttribute)des[0]).Description;
}
entityInfos.Add(new EntityInfo()
{
EntityName = c.Name,
@ -564,6 +587,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
Type = c
});
}
return await Task.FromResult(entityInfos);
bool IsMyAttribute(Attribute[] o)
@ -630,7 +654,8 @@ public class SysDatabaseService : IDynamicApiController, ITransient
throw Oops.Oh("只支持 PostgreSQL 数据库 😁");
var npgsqlConn = new NpgsqlConnectionStringBuilder(_db.CurrentConnectionConfig.ConnectionString);
if (npgsqlConn == null || string.IsNullOrWhiteSpace(npgsqlConn.Host) || string.IsNullOrWhiteSpace(npgsqlConn.Username) || string.IsNullOrWhiteSpace(npgsqlConn.Password) || string.IsNullOrWhiteSpace(npgsqlConn.Database))
if (npgsqlConn == null || string.IsNullOrWhiteSpace(npgsqlConn.Host) || string.IsNullOrWhiteSpace(npgsqlConn.Username) || string.IsNullOrWhiteSpace(npgsqlConn.Password) ||
string.IsNullOrWhiteSpace(npgsqlConn.Database))
throw Oops.Oh("PostgreSQL 数据库配置错误");
// 确保备份目录存在

View File

@ -4,6 +4,8 @@
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using DbType = SqlSugar.DbType;
namespace Admin.NET.Core.Service;
/// <summary>
@ -12,13 +14,15 @@ namespace Admin.NET.Core.Service;
[ApiDescriptionSettings(Order = 255, Description = "数据库备份")]
public class SysDbBackupService : IDynamicApiController, ITransient
{
private readonly SqlSugarRepository<SysUser> _sysUserRep;
private readonly string backupDir;
private readonly ISqlSugarClient _db;
private readonly string _backupDir;
private readonly SysDatabaseService _databaseService;
public SysDbBackupService(SqlSugarRepository<SysUser> sysUserRep)
public SysDbBackupService(ISqlSugarClient db, SysDatabaseService databaseService)
{
_sysUserRep = sysUserRep;
backupDir = Path.Combine(App.WebHostEnvironment.WebRootPath, "DbBackup");
_db = db;
_databaseService = databaseService;
_backupDir = Path.Combine(App.WebHostEnvironment.WebRootPath, "DbBackup");
}
/// <summary>
@ -30,9 +34,9 @@ public class SysDbBackupService : IDynamicApiController, ITransient
{
try
{
if (!Directory.Exists(backupDir))
Directory.CreateDirectory(backupDir);
var fileList = Directory.GetFiles(backupDir);
if (!Directory.Exists(_backupDir))
Directory.CreateDirectory(_backupDir);
var fileList = Directory.GetFiles(_backupDir);
var dbBackupList = new List<DbBackupOutput>();
foreach (var item in fileList)
@ -45,6 +49,7 @@ public class SysDbBackupService : IDynamicApiController, ITransient
CreateTime = info.CreationTime
});
}
return dbBackupList;
}
catch
@ -59,9 +64,9 @@ public class SysDbBackupService : IDynamicApiController, ITransient
/// <returns></returns>
[ApiDescriptionSettings(Name = "Add"), HttpPost]
[DisplayName("备份数据库")]
public async Task AddBackup()
public async Task AddBackup([FromQuery] string configId)
{
await Backup();
await Backup(configId);
}
/// <summary>
@ -72,7 +77,7 @@ public class SysDbBackupService : IDynamicApiController, ITransient
[DisplayName("删除备份")]
public void DeleteBackup([FromQuery] string fileName)
{
var path = Path.Combine(backupDir, fileName);
var path = Path.Combine(_backupDir, fileName);
File.Delete(path);
}
@ -81,24 +86,50 @@ public class SysDbBackupService : IDynamicApiController, ITransient
/// </summary>
/// <returns></returns>
[NonAction]
public async Task Backup()
public async Task Backup(string configId)
{
await Task.Run(() =>
{
var options = App.GetOptions<DbConnectionOptions>();
foreach (var option in options.ConnectionConfigs)
{
var configId = option.ConfigId == null || string.IsNullOrWhiteSpace(option.ConfigId.ToString())
? SqlSugarConst.MainConfigId : option.ConfigId.ToString();
var options = App.GetOptions<DbConnectionOptions>();
var option = options.ConnectionConfigs.FirstOrDefault(u => u.ConfigId + "" == configId) ?? throw Oops.Bah(ErrorCodeEnum.D1002);
var db = _db.AsTenant().GetConnectionScope(configId);
// 备份mysql 其他数据库自行扩展
if (option?.DbType == SqlSugar.DbType.MySql)
// 扩展名
var ext = "bak";
switch (option.DbType)
{
case DbType.MySql:
ext = "sql";
break;
case DbType.SqlServer:
ext = "bak";
break;
case DbType.Sqlite:
ext = "db";
break;
case DbType.PostgreSQL:
ext = "sql";
break;
}
var path = Path.Combine(_backupDir, $"{db.Ado.Connection.Database}_{DateTime.Now:yyyyMMddHHmmss}.{ext}");
// 备份数据库
switch (option.DbType)
{
case DbType.MySql or DbType.Sqlite or DbType.SqlServer:
await Task.Run(() => { db.DbMaintenance.BackupDataBase(db.Ado.Connection.Database, path); });
break;
case DbType.PostgreSQL:
{
var path = Path.Combine(backupDir, $"{configId}-{DateTime.Now:yyyyMMddHHmmss}.sql");
_sysUserRep.Context.DbMaintenance.BackupDataBase(_sysUserRep.Context.Ado.Connection.Database, path);
var fileStreamResult = (FileStreamResult)(await _databaseService.BackupDatabase());
// 将 fileStreamResult 保存为文件
await using var fileStream = new FileStream(path, FileMode.Create);
await fileStreamResult.FileStream.CopyToAsync(fileStream);
await fileStream.FlushAsync();
break;
}
}
});
default:
throw Oops.Bah(ErrorCodeEnum.db1004, option.DbType);
}
}
/// <summary>
@ -108,7 +139,7 @@ public class SysDbBackupService : IDynamicApiController, ITransient
[NonAction]
public void DeleteExpiredDbFile(int day = 7)
{
var list = Directory.GetFiles(backupDir);
var list = Directory.GetFiles(_backupDir);
foreach (var item in list)
{
var info = new FileInfo(item);
@ -120,7 +151,7 @@ public class SysDbBackupService : IDynamicApiController, ITransient
}
catch (Exception)
{
continue;
// ignored
}
}
}

View File

@ -18,6 +18,7 @@ import { Configuration } from '../configuration';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
import { AdminNETResultListDbColumnOutput } from '../models';
import { AdminNETResultListDbOutput } from '../models';
import { AdminNETResultListDbTableInfo } from '../models';
import { AdminNETResultListString } from '../models';
import { AdminNETResultString } from '../models';
@ -989,7 +990,7 @@ export const SysDatabaseApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysDatabaseListGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultListString>>> {
async apiSysDatabaseListGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultListDbOutput>>> {
const localVarAxiosArgs = await SysDatabaseApiAxiosParamCreator(configuration).apiSysDatabaseListGet(options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
@ -1179,7 +1180,7 @@ export const SysDatabaseApiFactory = function (configuration?: Configuration, ba
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysDatabaseListGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListString>> {
async apiSysDatabaseListGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListDbOutput>> {
return SysDatabaseApiFp(configuration).apiSysDatabaseListGet(options).then((request) => request(axios, basePath));
},
/**
@ -1362,7 +1363,7 @@ export class SysDatabaseApi extends BaseAPI {
* @throws {RequiredError}
* @memberof SysDatabaseApi
*/
public async apiSysDatabaseListGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListString>> {
public async apiSysDatabaseListGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListDbOutput>> {
return SysDatabaseApiFp(this.configuration).apiSysDatabaseListGet(options).then((request) => request(this.axios, this.basePath));
}
/**

View File

@ -27,10 +27,11 @@ export const SysDbBackupApiAxiosParamCreator = function (configuration?: Configu
/**
*
* @summary 🔖
* @param {string} [configId]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
apiSysDbBackupAddPost: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
apiSysDbBackupAddPost: async (configId?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/api/sysDbBackup/add`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
@ -51,6 +52,10 @@ export const SysDbBackupApiAxiosParamCreator = function (configuration?: Configu
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
}
if (configId !== undefined) {
localVarQueryParameter['configId'] = configId;
}
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
query.set(key, localVarQueryParameter[key]);
@ -170,11 +175,12 @@ export const SysDbBackupApiFp = function(configuration?: Configuration) {
/**
*
* @summary 🔖
* @param {string} [configId]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysDbBackupAddPost(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
const localVarAxiosArgs = await SysDbBackupApiAxiosParamCreator(configuration).apiSysDbBackupAddPost(options);
async apiSysDbBackupAddPost(configId?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
const localVarAxiosArgs = await SysDbBackupApiAxiosParamCreator(configuration).apiSysDbBackupAddPost(configId, options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
@ -219,11 +225,12 @@ export const SysDbBackupApiFactory = function (configuration?: Configuration, ba
/**
*
* @summary 🔖
* @param {string} [configId]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysDbBackupAddPost(options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
return SysDbBackupApiFp(configuration).apiSysDbBackupAddPost(options).then((request) => request(axios, basePath));
async apiSysDbBackupAddPost(configId?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
return SysDbBackupApiFp(configuration).apiSysDbBackupAddPost(configId, options).then((request) => request(axios, basePath));
},
/**
*
@ -257,12 +264,13 @@ export class SysDbBackupApi extends BaseAPI {
/**
*
* @summary 🔖
* @param {string} [configId]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SysDbBackupApi
*/
public async apiSysDbBackupAddPost(options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
return SysDbBackupApiFp(this.configuration).apiSysDbBackupAddPost(options).then((request) => request(this.axios, this.basePath));
public async apiSysDbBackupAddPost(configId?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
return SysDbBackupApiFp(this.configuration).apiSysDbBackupAddPost(configId, options).then((request) => request(this.axios, this.basePath));
}
/**
*

View File

@ -0,0 +1,71 @@
/* tslint:disable */
/* eslint-disable */
/**
* Admin.NET
* .NET <br/><u><b><font color='FF0000'> 👮</font></b></u>
*
* OpenAPI spec version: 1.0.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { DbOutput } from './db-output';
/**
*
*
* @export
* @interface AdminNETResultListDbOutput
*/
export interface AdminNETResultListDbOutput {
/**
*
*
* @type {number}
* @memberof AdminNETResultListDbOutput
*/
code?: number;
/**
* successwarningerror
*
* @type {string}
* @memberof AdminNETResultListDbOutput
*/
type?: string | null;
/**
*
*
* @type {string}
* @memberof AdminNETResultListDbOutput
*/
message?: string | null;
/**
*
*
* @type {Array<DbOutput>}
* @memberof AdminNETResultListDbOutput
*/
result?: Array<DbOutput> | null;
/**
*
*
* @type {any}
* @memberof AdminNETResultListDbOutput
*/
extras?: any | null;
/**
*
*
* @type {Date}
* @memberof AdminNETResultListDbOutput
*/
time?: Date;
}

View File

@ -0,0 +1,34 @@
/* tslint:disable */
/* eslint-disable */
/**
* Admin.NET
* .NET <br/><u><b><font color='FF0000'> 👮</font></b></u>
*
* OpenAPI spec version: 1.0.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/**
*
*
* @export
* @interface DbOutput
*/
export interface DbOutput {
/**
* @type {string}
* @memberof DbOutput
*/
configId?: string | null;
/**
* @type {string}
* @memberof DbOutput
*/
dbName?: string | null;
}

View File

@ -25,6 +25,7 @@ export enum GenericParameterAttributes {
NUMBER_4 = 4,
NUMBER_8 = 8,
NUMBER_16 = 16,
NUMBER_28 = 28
NUMBER_28 = 28,
NUMBER_32 = 32
}

View File

@ -46,6 +46,7 @@ export * from './admin-netresult-list-const-output';
export * from './admin-netresult-list-database-output';
export * from './admin-netresult-list-db-backup-output';
export * from './admin-netresult-list-db-column-output';
export * from './admin-netresult-list-db-output';
export * from './admin-netresult-list-db-table-info';
export * from './admin-netresult-list-enum-entity';
export * from './admin-netresult-list-enum-type-output';
@ -181,6 +182,7 @@ export * from './db-backup-output';
export * from './db-column-input';
export * from './db-column-output';
export * from './db-object-type';
export * from './db-output';
export * from './db-table-info';
export * from './db-table-input';
export * from './db-type';

View File

@ -61,12 +61,6 @@ export interface TypeInfo {
*/
metadataToken?: number;
/**
* @type {boolean}
* @memberof TypeInfo
*/
isInterface?: boolean;
/**
* @type {MemberTypes}
* @memberof TypeInfo
@ -103,6 +97,12 @@ export interface TypeInfo {
*/
module?: Module;
/**
* @type {boolean}
* @memberof TypeInfo
*/
isInterface?: boolean;
/**
* @type {boolean}
* @memberof TypeInfo

View File

@ -55,12 +55,6 @@ export interface Type {
*/
metadataToken?: number;
/**
* @type {boolean}
* @memberof Type
*/
isInterface?: boolean;
/**
* @type {MemberTypes}
* @memberof Type
@ -97,6 +91,12 @@ export interface Type {
*/
module?: Module;
/**
* @type {boolean}
* @memberof Type
*/
isInterface?: boolean;
/**
* @type {boolean}
* @memberof Type

View File

@ -3,10 +3,21 @@
<el-card class="full-table" shadow="hover" style="margin-top: 5px">
<vxe-grid ref="xGrid" class="xGrid-style" v-bind="options">
<template #toolbar_buttons>
<el-button type="primary" icon="ele-Plus" @click="handleAdd" v-auth="'dbBackup/add'" :loading="loading"> 新增 </el-button>
<el-form :inlineMessage="true" label-width="auto" style="flex: 1 1 0%">
<el-row :gutter="10">
<el-col class="mb5" :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
<el-form-item label="库名" prop="configId">
<el-select v-model="state.configId" placeholder="库名" filterable>
<el-option v-for="item in state.dbData" :key="item.configId" :label="`${item.dbName}(${item.configId})`" :value="item.configId" />
</el-select>
</el-form-item>
</el-col>
<el-col class="mb5" :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
<el-button type="primary" icon="ele-Plus" @click="handleAdd" v-auth="'dbBackup/add'" :loading="state.loading"> 新增 </el-button>
</el-col>
</el-row>
</el-form>
</template>
<template #toolbar_tools> </template>
<template #row_createTime="{ row }">
<el-tooltip :content="row.createTime" placement="top">
<span>{{ formatPast(row.createTime) }}</span>
@ -16,9 +27,7 @@
<span>{{ (row.size / 1024 / 1024).toFixed(2) }} MB</span>
</template>
<template #row_buttons="{ row }">
<el-tooltip content="删除" placement="top">
<el-button icon="ele-Delete" text type="danger" v-auth="'dbBackup/delete'" @click="handleDelete(row)"> </el-button>
</el-tooltip>
<el-button icon="ele-Delete" text type="danger" v-auth="'dbBackup/delete'" @click="handleDelete(row)"> 删除 </el-button>
</template>
</vxe-grid>
</el-card>
@ -26,16 +35,22 @@
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { onMounted, reactive, ref } from 'vue';
import { VxeGridInstance } from 'vxe-table';
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
import { formatPast } from '/@/utils/formatTime';
import { getAPI } from '/@/utils/axios-utils';
import { SysDbBackupApi } from '/@/api-services/api';
import { SysDatabaseApi, SysDbBackupApi } from '/@/api-services/api';
import { ElMessage, ElMessageBox } from 'element-plus';
const xGrid = ref<VxeGridInstance>();
const loading = ref(false);
const state = reactive({
loading: false,
dbData: [] as any,
configId: '',
});
//
const options = useVxeTable(
{
@ -56,29 +71,61 @@ const options = useVxeTable(
//
pagerConfig: { enabled: false },
//
toolbarConfig: { enabled: false },
toolbarConfig: { enabled: true },
}
);
//
onMounted(async () => {
options.loading = true;
let res = await getAPI(SysDatabaseApi).apiSysDatabaseListGet();
state.dbData = res.data.result;
options.loading = false;
});
// api
const handleQueryApi = async () => {
return await getAPI(SysDbBackupApi).apiSysDbBackupListGet();
};
//
//
const handleAdd = async () => {
loading.value = true;
await getAPI(SysDbBackupApi).apiSysDbBackupAddPost();
loading.value = false;
await xGrid.value?.commitProxy('reload');
try {
if (!state.configId) {
ElMessage.warning('请选择库名');
return;
}
await ElMessageBox.confirm('确定要新增备份吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
state.loading = true;
await getAPI(SysDbBackupApi).apiSysDbBackupAddPost(state.configId);
await xGrid.value?.commitProxy('reload');
} finally {
state.loading = false;
}
};
//
const handleDelete = async (row: any) => {
loading.value = true;
await getAPI(SysDbBackupApi).apiSysDbBackupDeletePost(row.fileName);
loading.value = false;
await xGrid.value?.commitProxy('reload');
try {
await ElMessageBox.confirm(`确定要删除备份:【${row.fileName}】吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
state.loading = true;
await getAPI(SysDbBackupApi).apiSysDbBackupDeletePost(row.fileName);
await xGrid.value?.commitProxy('reload');
} finally {
state.loading = false;
}
};
</script>

View File

@ -6,7 +6,7 @@
<el-col class="mb5" :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
<el-form-item label="库名" prop="configId">
<el-select v-model="state.configId" placeholder="库名" filterable @change="handleQueryTable">
<el-option v-for="item in state.dbData" :key="item" :label="item" :value="item" />
<el-option v-for="item in state.dbData" :key="item.configId" :label="`${item.dbName}(${item.configId})`" :value="item.configId" />
</el-select>
</el-form-item>
</el-col>