😎1、优化自定义查询 2、升级依赖

This commit is contained in:
zuohuaijun 2025-09-28 11:59:46 +08:00
parent b39592fdcd
commit be7cd177d1
17 changed files with 1090 additions and 407 deletions

View File

@ -21,16 +21,16 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="4.0.0" />
<PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="4.0.1" />
<PackageReference Include="AlipaySDKNet.Standard" Version="4.9.809" />
<PackageReference Include="AngleSharp" Version="1.3.0" />
<PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" Aliases="BouncyCastleV2" />
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.1.7" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.126" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.126" />
<PackageReference Include="Furion.Pure" Version="4.9.7.126" />
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.1.8" />
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.127" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.127" />
<PackageReference Include="Furion.Pure" Version="4.9.7.127" />
<PackageReference Include="Hardware.Info" Version="101.1.0" />
<PackageReference Include="Hashids.net" Version="1.7.0" />
<PackageReference Include="IPTools.China" Version="1.6.0" />
@ -51,9 +51,9 @@
<PackageReference Include="RabbitMQ.Client" Version="7.1.2" />
<PackageReference Include="SixLabors.ImageSharp.Web" Version="3.2.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.11.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.13.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.14.0" />
<PackageReference Include="SqlSugar.MongoDbCore" Version="5.1.4.264" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.202" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.205" />
<PackageReference Include="SSH.NET" Version="2025.0.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.7" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />

View File

@ -78,8 +78,9 @@ public sealed class RequiredIFAttribute(
/// <summary>
/// 判断是否需要进行验证
/// </summary>
/// <param name="targetValue">依赖属性的值</param>
/// <returns>是否需要验证</returns>
/// <param name="targetValue"></param>
/// <param name="dictAttr"></param>
/// <returns></returns>
private bool ShouldValidate(object targetValue, DictAttribute dictAttr)
{
switch (Comparison)

View File

@ -8,21 +8,41 @@ namespace Admin.NET.Core.Service;
public class GenerateSQLInput
{
public List<ColumnsSQL> Columns { get; set; }
public List<SelectConfig> SelectConfigs { get; set; }
public List<ConditionsSQL> Conditions { get; set; }
public List<JoinConfig> JoinConfigs { get; set; }
public List<OrdersSQL> Orders { get; set; }
public List<WhereConfig> WhereConfigs { get; set; }
public List<OrderConfig> OrderConfigs { get; set; }
}
public class ColumnsSQL
public class SelectConfig
{
public string Table { get; set; }
public string Column { get; set; }
}
public class ConditionsSQL
public class JoinConfig
{
public string JoinType { get; set; }
public string LeftTable { get; set; }
public string RightTable { get; set; }
public List<JoinConfig_TableColumn> TableColumns { get; set; }
}
public class JoinConfig_TableColumn
{
public string LeftTableColumn { get; set; }
public string RightTableColumn { get; set; }
}
public class WhereConfig
{
public string Table { get; set; }
@ -31,9 +51,13 @@ public class ConditionsSQL
public string Operator { get; set; }
public string Value { get; set; }
public string DataType { get; set; }
public string JoinType { get; set; }
}
public class OrdersSQL
public class OrderConfig
{
public string Table { get; set; }

View File

@ -0,0 +1,275 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
public static class BuildSQL
{
public static string Build(GenerateSQLInput config)
{
if (config.SelectConfigs == null || !config.SelectConfigs.Any())
throw new ArgumentException("至少需要一个选择配置");
StringBuilder sql = new StringBuilder();
// SELECT clause
sql.Append("SELECT ");
sql.Append(string.Join(", ", config.SelectConfigs.Select(sc =>
$"{sc.Table}.{sc.Column}")));
sql.AppendLine();
// 确定主表(出现频率最高的表)
var mainTable = DetermineMainTable(config);
sql.AppendLine($"FROM {mainTable}");
// 收集所有涉及的表
var allTables = CollectAllTables(config);
var joinedTables = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { mainTable };
// 处理JOIN关系 - 优化后的逻辑
ProcessJoins(sql, config.JoinConfigs, joinedTables, allTables);
// 添加剩余未连接的表使用CROSS JOIN
AddRemainingTables(sql, allTables, joinedTables);
// WHERE子句
ProcessWhereClause(sql, config.WhereConfigs);
// ORDER BY子句
ProcessOrderByClause(sql, config.OrderConfigs);
return sql.ToString();
}
private static string DetermineMainTable(GenerateSQLInput config)
{
// 统计所有表出现的频率
var tableFrequency = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
// 统计SELECT中的表
foreach (var sc in config.SelectConfigs)
{
if (tableFrequency.ContainsKey(sc.Table))
tableFrequency[sc.Table]++;
else
tableFrequency[sc.Table] = 1;
}
// 统计JOIN中的表
foreach (var jc in config.JoinConfigs)
{
if (tableFrequency.ContainsKey(jc.LeftTable))
tableFrequency[jc.LeftTable]++;
else
tableFrequency[jc.LeftTable] = 1;
if (tableFrequency.ContainsKey(jc.RightTable))
tableFrequency[jc.RightTable]++;
else
tableFrequency[jc.RightTable] = 1;
}
// 统计WHERE中的表
foreach (var wc in config.WhereConfigs)
{
if (tableFrequency.ContainsKey(wc.Table))
tableFrequency[wc.Table]++;
else
tableFrequency[wc.Table] = 1;
}
// 统计ORDER BY中的表
foreach (var oc in config.OrderConfigs)
{
if (tableFrequency.ContainsKey(oc.Table))
tableFrequency[oc.Table]++;
else
tableFrequency[oc.Table] = 1;
}
// 选择出现频率最高的表作为主表
return tableFrequency.OrderByDescending(kv => kv.Value)
.ThenBy(kv => kv.Key)
.FirstOrDefault().Key;
}
private static HashSet<string> CollectAllTables(GenerateSQLInput config)
{
var allTables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
config.SelectConfigs?.ForEach(sc => allTables.Add(sc.Table));
config.JoinConfigs?.ForEach(jc =>
{
allTables.Add(jc.LeftTable);
allTables.Add(jc.RightTable);
});
config.WhereConfigs?.ForEach(wc => allTables.Add(wc.Table));
config.OrderConfigs?.ForEach(oc => allTables.Add(oc.Table));
return allTables;
}
private static void ProcessJoins(StringBuilder sql, List<JoinConfig> joinConfigs,
HashSet<string> joinedTables, HashSet<string> allTables)
{
if (joinConfigs == null) return;
// 使用队列处理所有JOIN关系
var joinQueue = new Queue<JoinConfig>(joinConfigs);
var processedJoins = new HashSet<JoinConfig>();
bool progressMade;
do
{
progressMade = false;
var currentQueue = new Queue<JoinConfig>(joinQueue);
joinQueue.Clear();
while (currentQueue.Count > 0)
{
var join = currentQueue.Dequeue();
// 如果右表已经连接,跳过
if (joinedTables.Contains(join.RightTable))
{
processedJoins.Add(join);
continue;
}
// 如果左表已连接可以处理这个JOIN
if (joinedTables.Contains(join.LeftTable))
{
// 构建ON条件
var onConditions = join.TableColumns.Select(tc =>
$"{join.LeftTable}.{tc.LeftTableColumn} = " +
$"{join.RightTable}.{tc.RightTableColumn}");
sql.AppendLine($"{join.JoinType} {join.RightTable} ON {string.Join(" AND ", onConditions)}");
joinedTables.Add(join.RightTable);
processedJoins.Add(join);
progressMade = true;
}
else
{
// 左表未连接,放回队列稍后处理
joinQueue.Enqueue(join);
}
}
// 如果还有未处理的JOIN但队列未变化尝试添加缺失的表
if (!progressMade && joinQueue.Count > 0)
{
var nextJoin = joinQueue.Peek();
// 如果左表不在已连接表中,尝试添加它
if (!joinedTables.Contains(nextJoin.LeftTable) && allTables.Contains(nextJoin.LeftTable))
{
sql.AppendLine($", {nextJoin.LeftTable}");
joinedTables.Add(nextJoin.LeftTable);
progressMade = true;
}
}
}
while (progressMade && joinQueue.Count > 0);
// 处理剩余无法连接的JOIN
foreach (var join in joinQueue)
{
// 如果右表已经连接,跳过
if (joinedTables.Contains(join.RightTable)) continue;
// 尝试添加左表
if (!joinedTables.Contains(join.LeftTable))
{
sql.AppendLine($", {join.LeftTable}");
joinedTables.Add(join.LeftTable);
}
// 构建ON条件
var onConditions = join.TableColumns.Select(tc =>
$"{join.LeftTable}.{tc.LeftTableColumn} = " +
$"{join.RightTable}.{tc.RightTableColumn}");
sql.AppendLine($"{join.JoinType} {join.RightTable} ON {string.Join(" AND ", onConditions)}");
joinedTables.Add(join.RightTable);
}
}
private static void AddRemainingTables(StringBuilder sql, HashSet<string> allTables, HashSet<string> joinedTables)
{
var remainingTables = allTables.Except(joinedTables, StringComparer.OrdinalIgnoreCase).ToList();
if (!remainingTables.Any()) return;
foreach (var table in remainingTables)
{
sql.AppendLine($", {table}");
joinedTables.Add(table);
}
}
private static void ProcessWhereClause(StringBuilder sql, List<WhereConfig> whereConfigs)
{
if (whereConfigs == null || !whereConfigs.Any()) return;
sql.Append("WHERE ");
foreach (var where in whereConfigs)
{
string formattedValue = FormatValue(where.Value, where.DataType, where.Operator);
sql.Append($"{where.Table}.{where.Column} " + $"{where.Operator} {formattedValue} {where.JoinType} ");
}
// 移除最后一个逻辑运算符
sql.Length -= 4;
sql.AppendLine();
}
private static void ProcessOrderByClause(StringBuilder sql, List<OrderConfig> orderConfigs)
{
if (orderConfigs == null || !orderConfigs.Any()) return;
sql.Append("ORDER BY ");
sql.Append(string.Join(", ", orderConfigs.OrderBy(oc => oc.Sort).Select(oc => $"{oc.Table}.{oc.Column}")));
}
/// <summary>
/// WHERE 数值格式化
/// </summary>
/// <param name="value"></param>
/// <param name="dataType"></param>
/// <param name="operatorType"></param>
/// <returns></returns>
private static string FormatValue(object value, string dataType, string operatorType)
{
if (value == null) return "NULL";
string valueStr = value.ToString();
if (string.IsNullOrWhiteSpace(valueStr)) return "NULL";
// 根据数据类型和操作符格式化值
if (dataType?.ToLower().Contains("varchar") == true ||
dataType?.ToLower().Contains("text") == true ||
dataType?.ToLower().Contains("bool") == true ||
dataType?.ToLower().Contains("datetime") == true ||
dataType?.ToLower().Contains("timestamp") == true ||
value is string)
{
// 对于LIKE操作符添加通配符
if (operatorType?.ToUpper() == "LIKE")
{
return $"'%{valueStr}%'";
}
else
{
return $"'{valueStr}'";
}
}
else
{
// 数字类型直接返回
return valueStr;
}
}
}

View File

@ -631,54 +631,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
[DisplayName("生成SQL语句")]
public async Task<string> GenerateSQL(GenerateSQLInput input)
{
if (input.Columns == null || input.Columns.Count <= 0) throw Oops.Oh("至少需要设置一个列");
// 收集所有表名从columns、conditions、orders中
var tableNames = new HashSet<string>();
foreach (var col in input.Columns)
tableNames.Add(col.Table);
foreach (var cond in input.Conditions)
tableNames.Add(cond.Table);
foreach (var ord in input.Orders)
tableNames.Add(ord.Table);
if (tableNames == null || tableNames.Count == 0)
throw Oops.Oh("至少需要一个表");
// 构建 SELECT 子句
var selectItems = input.Columns.Select(u => $"{u.Table}.{u.Column}");
var selectString = "SELECT " + string.Join(", ", selectItems);
// 构建 FROM 子句
var fromString = "FROM " + string.Join(", ", tableNames);
// 组合SQL语句
var sql = new StringBuilder();
sql.Append(selectString);
sql.Append(" ");
sql.Append(fromString);
if (input.Conditions != null && input.Conditions.Count > 0)
{
// 构建WHERE子句
var whereConditions = input.Conditions.Where(u => !string.IsNullOrWhiteSpace(u.Table) && !string.IsNullOrWhiteSpace(u.Column)).Select(u =>
{
var valueStr = u.Value is string ? $"'{u.Value}'" : u.Value.ToString(); // 处理值:如果是字符串则需要加单引号
return $"{u.Table}.{u.Column} {u.Operator} {valueStr}";
});
var whereString = "WHERE " + string.Join(" AND ", whereConditions);
sql.Append(" ");
sql.Append(whereString);
}
if (input.Orders != null && input.Orders.Count > 0)
{
// 构建 ORDER BY 子句
var orderItems = input.Orders.OrderBy(u => u.Sort).Where(u => !string.IsNullOrWhiteSpace(u.Table) && !string.IsNullOrWhiteSpace(u.Column)).Select(u => $"{u.Table}.{u.Column} ASC"); // 按照sort值从小到大排序确定列的先后顺序
var orderByString = "ORDER BY " + string.Join(", ", orderItems);
sql.Append(" ");
sql.Append(orderByString);
}
return await Task.FromResult(sql.ToString());
return await Task.FromResult(BuildSQL.Build(input));
}
/// <summary>
@ -690,7 +643,10 @@ public class SysDatabaseService : IDynamicApiController, ITransient
public async Task<DataTable> ExecuteSQL(GenerateSQLInput input)
{
var sql = await GenerateSQL(input);
var dt = _db.Ado.GetDataTable(sql);
if (Regex.IsMatch(sql, @"\b(delete|update)\b", RegexOptions.IgnoreCase))
throw Oops.Oh("非法SQL语句已停止执行");
var dt = await _db.Ado.GetDataTableAsync(sql);
return dt;
}
}

View File

@ -14,8 +14,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Furion.Xunit" Version="4.9.7.126" />
<PackageReference Include="Furion.Pure" Version="4.9.7.126">
<PackageReference Include="Furion.Xunit" Version="4.9.7.127" />
<PackageReference Include="Furion.Pure" Version="4.9.7.127">
<ExcludeAssets>compile</ExcludeAssets>
</PackageReference>
<PackageReference Include="xunit.assert" Version="2.9.3" />

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36511.14 d17.14
VisualStudioVersion = 17.14.36511.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Application", "Admin.NET.Application\Admin.NET.Application.csproj", "{C3F5AEC5-ACEE-4109-94E3-3F981DC18268}"
EndProject

View File

@ -2,7 +2,7 @@
"name": "admin.net.pro",
"type": "module",
"version": "2.4.33",
"lastBuildTime": "2025.09.23",
"lastBuildTime": "2025.09.28",
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
"author": "zuohuaijun",
"license": "MIT",
@ -21,7 +21,7 @@
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@logicflow/core": "^2.1.2",
"@logicflow/extension": "^2.1.3",
"@logicflow/extension": "^2.1.4",
"@microsoft/signalr": "^9.0.6",
"@vue-office/docx": "^1.6.3",
"@vue-office/excel": "^1.7.14",
@ -39,7 +39,7 @@
"cropperjs": "^1.6.2",
"crypto-js": "^4.2.0",
"echarts": "^6.0.0",
"element-plus": "^2.11.3",
"element-plus": "^2.11.4",
"exceljs": "^4.4.0",
"flag-icons": "^7.5.0",
"franc": "^6.2.0",
@ -69,7 +69,7 @@
"uuid": "^13.0.0",
"vcrontab-3": "^3.3.22",
"vform3-builds": "^3.0.10",
"vue": "^3.5.21",
"vue": "^3.5.22",
"vue-clipboard3": "^2.0.0",
"vue-demi": "0.14.10",
"vue-draggable-plus": "^0.6.0",
@ -81,8 +81,8 @@
"vue-router": "^4.5.1",
"vue-signature-pad": "^3.0.2",
"vue3-tree-org": "^4.2.2",
"vxe-pc-ui": "^4.9.34",
"vxe-table": "^4.16.18",
"vxe-pc-ui": "^4.9.38",
"vxe-table": "^4.16.20",
"xe-utils": "^3.7.9",
"xlsx-js-style": "^1.2.0"
},
@ -97,7 +97,7 @@
"@typescript-eslint/parser": "^8.44.1",
"@vitejs/plugin-vue": "^6.0.1",
"@vitejs/plugin-vue-jsx": "^5.1.1",
"@vue/compiler-sfc": "^3.5.21",
"@vue/compiler-sfc": "^3.5.22",
"cli-progress": "^3.12.0",
"code-inspector-plugin": "^1.2.10",
"colors": "^1.4.0",
@ -108,7 +108,7 @@
"less": "^4.4.1",
"prettier": "^3.6.2",
"rollup-plugin-visualizer": "^6.0.3",
"sass": "^1.93.1",
"sass": "^1.93.2",
"terser": "^5.44.0",
"typescript": "^5.9.2",
"vite": "^7.1.7",

View File

@ -12,9 +12,10 @@
* Do not edit the class manually.
*/
import { ColumnsSQL } from './columns-sql';
import { ConditionsSQL } from './conditions-sql';
import { OrdersSQL } from './orders-sql';
import { JoinConfig } from './join-config';
import { OrderConfig } from './order-config';
import { SelectConfig } from './select-config';
import { WhereConfig } from './where-config';
/**
*
*
@ -24,20 +25,26 @@ import { OrdersSQL } from './orders-sql';
export interface GenerateSQLInput {
/**
* @type {Array<ColumnsSQL>}
* @type {Array<SelectConfig>}
* @memberof GenerateSQLInput
*/
columns?: Array<ColumnsSQL> | null;
selectConfigs?: Array<SelectConfig> | null;
/**
* @type {Array<ConditionsSQL>}
* @type {Array<JoinConfig>}
* @memberof GenerateSQLInput
*/
conditions?: Array<ConditionsSQL> | null;
joinConfigs?: Array<JoinConfig> | null;
/**
* @type {Array<OrdersSQL>}
* @type {Array<WhereConfig>}
* @memberof GenerateSQLInput
*/
orders?: Array<OrdersSQL> | null;
whereConfigs?: Array<WhereConfig> | null;
/**
* @type {Array<OrderConfig>}
* @memberof GenerateSQLInput
*/
orderConfigs?: Array<OrderConfig> | null;
}

View File

@ -188,9 +188,7 @@ export * from './code-gen-scene-enum';
export * from './column-custom-output';
export * from './column-output';
export * from './column-relation';
export * from './columns-sql';
export * from './compare-info';
export * from './conditions-sql';
export * from './const-output';
export * from './constructor-info';
export * from './copy-role-input';
@ -308,6 +306,8 @@ export * from './job-create-type-enum';
export * from './job-detail-input';
export * from './job-detail-output';
export * from './job-trigger-input';
export * from './join-config';
export * from './join-config-table-column';
export * from './key-value-pair-string-string';
export * from './llmmodel-input';
export * from './layout-kind';
@ -355,7 +355,7 @@ export * from './oauth-user-output';
export * from './online-user';
export * from './open-access-output';
export * from './order-by-type';
export * from './orders-sql';
export * from './order-config';
export * from './page-code-gen-input';
export * from './page-config-input';
export * from './page-dict-data-input';
@ -427,6 +427,7 @@ export * from './schema-serialization-mode';
export * from './search';
export * from './security-rule-set';
export * from './seed-type';
export * from './select-config';
export * from './select-list-group';
export * from './select-list-item';
export * from './send-subscribe-message-input';
@ -579,6 +580,7 @@ export * from './wechat-pay-para-input';
export * from './wechat-pay-para-output';
export * from './wechat-pay-transaction-input';
export * from './wechat-user-login';
export * from './where-config';
export * from './wx-open-id-login-input';
export * from './wx-open-id-output';
export * from './wx-phone-output';

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 JoinConfigTableColumn
*/
export interface JoinConfigTableColumn {
/**
* @type {string}
* @memberof JoinConfigTableColumn
*/
leftTableColumn?: string | null;
/**
* @type {string}
* @memberof JoinConfigTableColumn
*/
rightTableColumn?: string | null;
}

View File

@ -0,0 +1,47 @@
/* 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 { JoinConfigTableColumn } from './join-config-table-column';
/**
*
*
* @export
* @interface JoinConfig
*/
export interface JoinConfig {
/**
* @type {string}
* @memberof JoinConfig
*/
joinType?: string | null;
/**
* @type {string}
* @memberof JoinConfig
*/
leftTable?: string | null;
/**
* @type {string}
* @memberof JoinConfig
*/
rightTable?: string | null;
/**
* @type {Array<JoinConfigTableColumn>}
* @memberof JoinConfig
*/
tableColumns?: Array<JoinConfigTableColumn> | null;
}

View File

@ -16,25 +16,25 @@
*
*
* @export
* @interface OrdersSQL
* @interface OrderConfig
*/
export interface OrdersSQL {
export interface OrderConfig {
/**
* @type {string}
* @memberof OrdersSQL
* @memberof OrderConfig
*/
table?: string | null;
/**
* @type {string}
* @memberof OrdersSQL
* @memberof OrderConfig
*/
column?: string | null;
/**
* @type {number}
* @memberof OrdersSQL
* @memberof OrderConfig
*/
sort?: number;
}

View File

@ -16,19 +16,19 @@
*
*
* @export
* @interface ColumnsSQL
* @interface SelectConfig
*/
export interface ColumnsSQL {
export interface SelectConfig {
/**
* @type {string}
* @memberof ColumnsSQL
* @memberof SelectConfig
*/
table?: string | null;
/**
* @type {string}
* @memberof ColumnsSQL
* @memberof SelectConfig
*/
column?: string | null;
}

View File

@ -16,31 +16,43 @@
*
*
* @export
* @interface ConditionsSQL
* @interface WhereConfig
*/
export interface ConditionsSQL {
export interface WhereConfig {
/**
* @type {string}
* @memberof ConditionsSQL
* @memberof WhereConfig
*/
table?: string | null;
/**
* @type {string}
* @memberof ConditionsSQL
* @memberof WhereConfig
*/
column?: string | null;
/**
* @type {string}
* @memberof ConditionsSQL
* @memberof WhereConfig
*/
operator?: string | null;
/**
* @type {string}
* @memberof ConditionsSQL
* @memberof WhereConfig
*/
value?: string | null;
/**
* @type {string}
* @memberof WhereConfig
*/
dataType?: string | null;
/**
* @type {string}
* @memberof WhereConfig
*/
joinType?: string | null;
}

View File

@ -1,182 +1,351 @@
<template>
<div class="reportCustomQuery-container">
<el-card shadow="hover" style="margin-bottom: 10px">
<!-- <template #header>
<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Setting /> </el-icon>
<span> 条件配置 </span>
</template> -->
<splitpanes class="default-theme" horizontal>
<pane size="40">
<el-card shadow="hover">
<el-row :gutter="10" style="text-align: center">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-divider>
显示列表配置
<span style="margin-left: 15px">
<el-button type="primary" icon="ele-Plus" circle @click="addDbConfig"></el-button>
<el-button type="danger" icon="ele-Delete" circle @click="clearDbConfig"></el-button>
</span>
</el-divider>
<div class="config-container">
<div v-for="(selectConfig, index) in state.dbConfigs" :key="index" class="config-item-db">
<div class="config-item-left" style="flex: 1">
<span>表名</span>
<el-select v-model="selectConfig.selectedTables" @change="(val: string) => onTableChange(val, selectConfig)">
<el-option v-for="item in state.dbTables" :key="item.name" :label="`${item.description ? item.description : ''}[${item.name}]`" :value="item.name" />
</el-select>
</div>
<div class="config-item-right" style="flex: 1">
<span>列名</span>
<el-select v-model="selectConfig.selectedColumns" multiple filterable @change="(val: string[]) => onColumnChange(val, selectConfig)">
<el-option
v-for="column in selectConfig.dbColumns"
:key="column.dbColumnName"
:label="`${column.tableDescription ? column.tableDescription : ''}.${column.columnDescription ? column.columnDescription : ''}[${column.tableName}.${column.dbColumnName}]`"
:value="`${column.tableName}.${column.dbColumnName}`"
/>
</el-select>
</div>
<!-- 删除按钮 -->
<div class="config-item-button">
<el-button type="danger" icon="ele-CloseBold" plain circle @click="removeDbConfig(index)"></el-button>
</div>
</div>
</div>
</el-col>
<el-divider>数据显示列配置</el-divider>
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="表名">
<el-select v-model="state.selectedTables" multiple filterable @change="onTableChange">
<el-option v-for="item in state.dbTables" :key="item.name" :label="`${item.description ? item.description : ''}[${item.name}]`" :value="item.name" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item label="列名">
<el-select v-model="state.selectedColumns" multiple filterable @change="onColumnsChange">
<el-option
v-for="column in state.dbColumns"
:key="column.dbColumnName"
:label="`${column.tableDescription ? column.tableDescription : ''}.${column.columnDescription ? column.columnDescription : ''}[${column.tableName}.${column.dbColumnName}]`"
:value="`${column.tableName}.${column.dbColumnName}`"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-row :gutter="10" style="text-align: center">
<!-- 连接关系配置 -->
<el-divider>
连接关系配置
<span style="margin-left: 15px">
<el-button type="primary" icon="ele-Plus" circle @click="addJoinConfig"></el-button>
<el-button type="danger" icon="ele-Delete" circle @click="clearJoinConfig"></el-button>
</span>
</el-divider>
<div class="config-container">
<div v-for="(joinConfig, index) in state.joinConfigs" :key="index" class="config-item-join">
<div class="config-item-left">
<!-- 连接关系选择项 -->
<el-select v-model="joinConfig.joinType" style="width: 100px">
<el-option label="左连接" value="LEFT JOIN" />
<el-option label="右连接" value="RIGHT JOIN" />
<el-option label="内连接" value="INNER JOIN" />
</el-select>
<!-- 左表选择项 -->
<el-select v-model="joinConfig.leftTable" style="flex: 1; margin-left: 10px">
<el-option v-for="item in state.dbTables" :key="item.name" :label="`${item.description ? item.description : ''}[${item.name}]`" :value="item.name" />
</el-select>
<!-- 右表选择项 -->
<el-select v-model="joinConfig.rightTable" style="flex: 1; margin-left: 10px">
<el-option v-for="item in state.dbTables" :key="item.name" :label="`${item.description ? item.description : ''}[${item.name}]`" :value="item.name" />
</el-select>
<!-- 操作按钮增加和删除 -->
<el-button-group style="margin-left: 10px">
<el-button type="primary" icon="ele-Plus" @click="addJoinTable(index)"></el-button>
<el-button type="danger" icon="ele-Close" @click="delJoinTable(index)"></el-button>
</el-button-group>
</div>
<div class="config-item-right" v-if="joinConfig.tableColumns.length > 0">
<div v-for="(item, kindex) in joinConfig.tableColumns" :key="kindex" class="config-item-right-item">
<!-- 左表字段下拉框 -->
<el-select v-model="item.leftTableColumn" @visible-change="(visible: boolean) => visible && getJoinTableColumns(joinConfig.leftTable, true, index)">
<el-option
v-for="column in joinConfig.leftTableColumns"
:key="column.dbColumnName"
:label="`${column.columnDescription ? column.columnDescription : ''}[${column.tableName}.${column.dbColumnName}]`"
:value="`${column.dbColumnName}`"
/>
</el-select>
<!-- 右表字段下拉框 -->
<el-select
v-model="item.rightTableColumn"
style="margin-left: 10px"
@visible-change="(visible: boolean) => visible && getJoinTableColumns(joinConfig.rightTable, false, index)"
>
<el-option
v-for="column in joinConfig.rightTableColumns"
:key="column.dbColumnName"
:label="`${column.columnDescription ? column.columnDescription : ''}[${column.tableName}.${column.dbColumnName}]`"
:value="`${column.dbColumnName}`"
/>
</el-select>
<el-button type="danger" icon="ele-CloseBold" plain circle @click="removeJoinColumn(index, kindex)" style="margin-left: 10px" />
</div>
</div>
</div>
</div>
</el-row>
</el-col>
</el-row>
<el-row :gutter="10" style="text-align: center">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<!-- 查询条件配置 -->
<el-divider>
查询条件配置
<el-button type="primary" icon="ele-Plus" plain circle @click="addCondition"></el-button>
<el-button type="danger" icon="ele-Delete" plain circle @click="clearCondition"></el-button>
</el-divider>
<div class="conditions-container">
<div v-for="(condition, index) in state.queryConditions" :key="index" class="condition-item">
<!-- 表选择项 -->
<el-select v-model="condition.table" @change="changeConditionTable(condition)" style="width: 400px">
<el-option v-for="item in state.dbTables" :key="item.name" :label="`${item.description ? item.description : ''}[${item.name}]`" :value="item.name" />
</el-select>
<!-- 列选择项 -->
<el-select v-model="condition.column" style="width: 400px" @change="changeConditionColumn(condition)">
<el-option
v-for="column in condition.columns"
:key="column.dbColumnName"
:label="`${column.columnDescription ? column.columnDescription : ''}[${column.dbColumnName}]`"
:value="`${column.tableName}.${column.dbColumnName}`"
/>
</el-select>
<!-- 操作符选择项 -->
<el-select v-model="condition.operator" style="width: 250px">
<el-option label="等于" value="=" />
<el-option label="不等于" value="!=" />
<el-option label="大于" value=">" />
<el-option label="小于" value="<" />
<el-option label="大于等于" value=">=" />
<el-option label="小于等于" value="<=" />
<el-option label="包含" value="LIKE" />
</el-select>
<!-- 值填写项 -->
<el-input-number v-if="condition.dataType == 'bigint' || condition.dataType == 'int8'" v-model="condition.value" controls-position="right" class="w100" />
<el-date-picker v-else-if="condition.dataType == 'datetime' || condition.dataType == 'timestamp'" v-model="condition.value" type="datetime" class="w100" />
<el-input v-else v-model="condition.value" />
<!-- 删除按钮 -->
<el-button type="danger" icon="ele-CloseBold" plain circle @click="removeCondition(index)"></el-button>
</div>
</div>
</el-col>
<el-row :gutter="10" style="text-align: center">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<!-- 查询条件配置 -->
<el-divider>
查询条件配置
<span style="margin-left: 15px">
<el-button type="primary" icon="ele-Plus" circle @click="addWhereConfig"></el-button>
<el-button type="danger" icon="ele-Delete" circle @click="clearWhereConfig"></el-button>
</span>
</el-divider>
<div class="config-container">
<div v-for="(condition, index) in state.whereConfigs" :key="index" class="config-item">
<!-- 逻辑操作符选择项 (隐藏第一个条件的逻辑操作符) -->
<el-select v-model="condition.joinType" style="width: 200px">
<el-option label="且" value="AND" />
<el-option label="或" value="OR" />
</el-select>
<!-- 表选择项 -->
<el-select v-model="condition.table" @change="changeWhereTable(condition)" style="width: 400px">
<el-option v-for="item in state.dbTables" :key="item.name" :label="`${item.description ? item.description : ''}[${item.name}]`" :value="item.name" />
</el-select>
<!-- 列选择项 -->
<el-select v-model="condition.column" style="width: 400px" @change="changeWhereColumn(condition)">
<el-option
v-for="column in condition.columns"
:key="column.dbColumnName"
:label="`${column.columnDescription ? column.columnDescription : ''}[${column.dbColumnName}]`"
:value="`${column.tableName}.${column.dbColumnName}`"
/>
</el-select>
<!-- 操作符选择项 -->
<el-select v-model="condition.operator" style="width: 250px">
<el-option label="等于" value="=" />
<el-option label="不等于" value="!=" />
<el-option label="大于" value=">" />
<el-option label="小于" value="<" />
<el-option label="大于等于" value=">=" />
<el-option label="小于等于" value="<=" />
<el-option label="包含" value="LIKE" />
</el-select>
<!-- 值填写项 -->
<el-input-number v-if="condition.dataType == 'bigint' || condition.dataType == 'int8'" v-model="condition.value" controls-position="right" class="w100" />
<el-date-picker
v-else-if="condition.dataType == 'datetime' || condition.dataType == 'timestamp'"
v-model="condition.value"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
class="w100"
/>
<el-input v-else v-model="condition.value" />
<!-- 删除按钮 -->
<el-button type="danger" icon="ele-CloseBold" plain circle @click="removeWhereConfig(index)"></el-button>
</div>
</div>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<!-- 数据排序配置 -->
<el-divider>
数据排序配置
<el-button type="primary" icon="ele-Plus" plain circle @click="addSortCondition"></el-button>
<el-button type="danger" icon="ele-Delete" plain circle @click="clearSortCondition"></el-button>
</el-divider>
<div class="conditions-container">
<div v-for="(sortCondition, index) in state.sortConditions" :key="index" class="condition-item">
<!-- 表选择项 -->
<el-select v-model="sortCondition.table" @change="changeConditionTable(sortCondition)">
<el-option v-for="item in state.dbTables" :key="item.name" :label="`${item.description ? item.description : ''}[${item.name}]`" :value="item.name" />
</el-select>
<!-- 列选择项 -->
<el-select v-model="sortCondition.column">
<el-option
v-for="column in sortCondition.columns"
:key="column.dbColumnName"
:label="`${column.columnDescription ? column.columnDescription : ''}[${column.dbColumnName}]`"
:value="`${column.tableName}.${column.dbColumnName}`"
/>
</el-select>
<!-- 排序序号 -->
<el-input-number v-model="sortCondition.sortOrder" style="width: 180px" controls-position="right" />
<!-- 删除按钮 -->
<el-button type="danger" icon="ele-CloseBold" plain circle @click="removeSortCondition(index)"></el-button>
</div>
</div>
</el-col>
</el-row>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<!-- 数据排序配置 -->
<el-divider>
数据排序配置
<span style="margin-left: 15px">
<el-button type="primary" icon="ele-Plus" circle @click="addSortConfig"></el-button>
<el-button type="danger" icon="ele-Delete" circle @click="clearSortConfig"></el-button>
</span>
</el-divider>
<div class="config-container">
<div v-for="(sortCondition, index) in state.orderConfigs" :key="index" class="config-item">
<!-- 表选择项 -->
<el-select v-model="sortCondition.table" @change="changeWhereTable(sortCondition)">
<el-option v-for="item in state.dbTables" :key="item.name" :label="`${item.description ? item.description : ''}[${item.name}]`" :value="item.name" />
</el-select>
<!-- 列选择项 -->
<el-select v-model="sortCondition.column">
<el-option
v-for="column in sortCondition.columns"
:key="column.dbColumnName"
:label="`${column.columnDescription ? column.columnDescription : ''}[${column.dbColumnName}]`"
:value="`${column.tableName}.${column.dbColumnName}`"
/>
</el-select>
<!-- 排序序号 -->
<el-input-number v-model="sortCondition.sort" style="width: 180px" controls-position="right" />
<!-- 删除按钮 -->
<el-button type="danger" icon="ele-CloseBold" plain circle @click="removeSortConfig(index)"></el-button>
</div>
</div>
</el-col>
</el-row>
<!-- 操作按钮 -->
<el-row :gutter="10" style="text-align: center">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-button icon="ele-View" type="primary" plain @click="generateSQL">预览SQL</el-button>
<el-button icon="ele-Position" type="primary" @click="executeSQL">执行查询</el-button>
<el-button icon="ele-FolderOpened" @click="openSchema">打开方案</el-button>
<el-button icon="ele-DocumentCopy" @click="saveSchema">保存方案</el-button>
</el-col>
</el-row>
<!-- 操作按钮 -->
<el-row :gutter="10" style="text-align: center">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-button icon="ele-View" type="primary" plain @click="generateSQL">预览SQL</el-button>
<el-button icon="ele-Position" type="primary" @click="executeSQL">执行查询</el-button>
<el-button icon="ele-FolderOpened" @click="openSchema">打开方案</el-button>
<el-button icon="ele-DocumentCopy" @click="saveSchema">保存方案</el-button>
</el-col>
</el-row>
<!-- SQL语句预览 -->
<el-row :gutter="10">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="">
<el-input v-model="state.sqlString" type="textarea" :rows="5" disabled style="color: red" />
</el-col>
</el-row>
</el-card>
<!-- SQL语句预览 -->
<el-row :gutter="10">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="">
<el-input v-model="state.sqlString" type="textarea" :rows="5" disabled style="color: red" />
</el-col>
</el-row>
</el-card>
</pane>
<!-- 查询结果展示 - 直接显示表格 -->
<el-card shadow="hover">
<template #header>
<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Tickets /> </el-icon>
<span> 查询结果 </span>
</template>
<el-table :data="formattedTableData" style="width: 100%" height="calc(100% - 30px)" v-if="state.selectedColumnDetails.length > 0">
<el-table-column v-for="(column, index) in state.selectedColumnDetails" :key="index" :label="`${column.tableDescription}.${column.columnDescription}`" :prop="`col${index}`"> </el-table-column>
</el-table>
</el-card>
<pane size="60">
<!-- 查询结果表格显示 -->
<vxe-grid ref="xGrid" class="xGrid-style" v-bind="options">
<template #toolbar_buttons>
<el-icon size="20" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Tickets /> </el-icon>
<span> 查询结果 </span>
</template>
<template #toolbar_tools> </template>
<template #empty>
<el-empty :image-size="200" />
</template>
</vxe-grid>
</pane>
</splitpanes>
</div>
</template>
<!-- 报表自定义查询 -->
<script lang="ts" setup name="sysReportCustomQuery">
import { reactive, onMounted, computed } from 'vue';
import { reactive, onMounted, computed, ref, watch } from 'vue';
import { ElMessage } from 'element-plus';
// import { format } from 'sql-formatter';
import { Splitpanes, Pane } from 'splitpanes';
import 'splitpanes/dist/splitpanes.css';
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
import { VxeGridInstance } from 'vxe-table';
import { getAPI } from '/@/utils/axios-utils';
import { SysDatabaseApi } from '/@/api-services/system/api';
import { DbTableInfo } from '/@/api-services/system/models';
import { DbColumnOutput, DbTableInfo } from '/@/api-services/system/models';
//
const state = reactive({
//
dbTables: [] as Array<DbTableInfo>,
dbColumns: [] as any,
selectedTables: [] as any,
selectedColumns: [] as any,
selectedColumnDetails: [] as any,
//
dbConfigs: [] as Array<{
selectedTables: string;
selectedColumns: any[];
dbColumns: any[];
}>,
selectConfigs: [] as any,
//
joinConfigs: [] as Array<{
joinType: 'LEFT JOIN'; // LEFTJOINRIGHTJOININNERJOIN
leftTable: ''; //
rightTable: ''; //
tableColumns: Array<{ leftTableColumn: string; rightTableColumn: string }>; //
leftTableColumns: any[];
rightTableColumns: any[];
}>,
//
queryConditions: [] as Array<{
whereConfigs: [] as Array<{
table: string;
column: string;
operator: string; //
value: string; //
columns: any;
dataType: string; //
joinType: string; //
}>,
//
sortConditions: [] as Array<{
orderConfigs: [] as Array<{
table: string;
column: string;
sortOrder: number;
sort: number; //
columns: any;
}>,
// SQL
sqlString: 'select * from xxx',
//
queryResults: [] as Array<{
columns: string[];
data: any[][];
}>,
queryResult: [] as any,
});
// xGrid
const xGrid = ref<VxeGridInstance>();
//
const dynamicColumns = computed(() => {
// -
const columns: any = [
{
field: 'seq',
type: 'seq',
title: '序号',
width: 60,
fixed: 'left',
},
];
// selectConfigs
if (state.selectConfigs && state.selectConfigs.length > 0) {
state.selectConfigs.forEach((config: any) => {
//
if (config.dbColumnName && config.tableDescription && config.columnDescription) {
columns.push({
field: config.dbColumnName,
title: `${config.tableDescription}.${config.columnDescription}`,
showOverflow: 'tooltip',
});
}
});
}
return columns;
});
// selectConfigs
watch(
() => state.selectConfigs,
() => {
//
if (xGrid.value) {
xGrid.value.loadColumn(dynamicColumns.value);
}
},
{ deep: true }
);
//
const options = useVxeTable<DbColumnOutput>(
{
id: 'sysCustomQuery',
name: '自定义查询',
columns: dynamicColumns.value as any,
},
{
//
pagerConfig: { enabled: false },
//
toolbarConfig: { export: true },
}
);
//
onMounted(async () => {
//
@ -187,39 +356,150 @@ onMounted(async () => {
state.dbTables.push(element);
}
});
updateSelectConfigs();
});
//
const onTableChange = async () => {
state.dbColumns = [];
if (!state.selectedTables) return;
//
const onTableChange = async (value: string, selectConfig: { selectedTables: string; selectedColumns: any[]; dbColumns: any[] }) => {
selectConfig.dbColumns = [];
if (!selectConfig.selectedTables) return;
state.selectedTables.forEach(async (element: string) => {
//
const tableInfo = state.dbTables.find((table) => table.name === element);
const res: any = await getAPI(SysDatabaseApi).apiSysDatabaseColumnListTableNameConfigIdGet(element, '1300000000001');
if (res.data.result && Array.isArray(res.data.result)) {
res.data.result.forEach((newColumn: any) => {
const columnWithTableDesc = {
...newColumn,
tableDescription: tableInfo ? tableInfo.description : '', //
};
state.dbColumns.push(columnWithTableDesc);
});
}
//
const tableInfo = state.dbTables.find((table) => table.name === value);
const res: any = await getAPI(SysDatabaseApi).apiSysDatabaseColumnListTableNameConfigIdGet(value, '1300000000001');
if (res.data.result && Array.isArray(res.data.result)) {
res.data.result.forEach((newColumn: any) => {
const columnWithTableDesc = {
...newColumn,
tableDescription: tableInfo ? tableInfo.description : '', //
};
selectConfig.dbColumns.push(columnWithTableDesc);
});
}
};
//
const onColumnChange = (val: string[], selectConfig: { selectedTables: string; selectedColumns: any[]; dbColumns: any[] }) => {
state.sqlString = '';
updateSelectConfigs();
};
const updateSelectConfigs = () => {
//
state.selectConfigs = [];
//
state.dbConfigs.forEach((config) => {
config.selectedColumns.forEach((colFullName: string) => {
// "."
const [tableName, dbColumnName] = colFullName.split('.');
// configdbColumns
if (tableName && dbColumnName) {
const matchedColumn = config.dbColumns.find((column: any) => column.tableName === tableName && column.dbColumnName === dbColumnName);
if (matchedColumn) {
state.selectConfigs.push(matchedColumn);
}
}
});
});
};
//
const changeConditionTable = async (condition: any) => {
//
const addDbConfig = () => {
state.dbConfigs.push({
selectedTables: '',
selectedColumns: [],
dbColumns: [],
});
updateSelectConfigs();
};
//
const removeDbConfig = (index: number) => {
state.dbConfigs.splice(index, 1);
updateSelectConfigs();
};
//
const clearDbConfig = () => {
state.dbConfigs = [];
state.selectConfigs = [];
updateSelectConfigs();
};
//
const addJoinConfig = () => {
state.joinConfigs.push({
joinType: 'LEFT JOIN',
leftTable: '',
rightTable: '',
tableColumns: [
{
leftTableColumn: '',
rightTableColumn: '',
},
],
leftTableColumns: [],
rightTableColumns: [],
});
};
//
const clearJoinConfig = () => {
state.joinConfigs = [];
};
//
const addJoinTable = (index: number) => {
state.joinConfigs[index].tableColumns.push({
leftTableColumn: '',
rightTableColumn: '',
});
};
//
const delJoinTable = (index: number) => {
state.joinConfigs.splice(index, 1);
};
//
const removeJoinColumn = (index: number, keyIndex: number) => {
state.joinConfigs[index].tableColumns.splice(keyIndex, 1);
};
//
const getJoinTableColumns = async (tableName: string, isLeftTable: boolean = true, joinIndex: number) => {
if (tableName) {
try {
const res: any = await getAPI(SysDatabaseApi).apiSysDatabaseColumnListTableNameConfigIdGet(tableName, '1300000000001');
const columns = res?.data?.result || [];
// isLeftTable
if (isLeftTable) {
state.joinConfigs[joinIndex].leftTableColumns = columns;
} else {
state.joinConfigs[joinIndex].rightTableColumns = columns;
}
return columns;
} catch (error) {
console.error('获取表字段失败:', error);
return [];
}
}
return [];
};
//
const changeWhereTable = async (condition: any) => {
const res: any = await getAPI(SysDatabaseApi).apiSysDatabaseColumnListTableNameConfigIdGet(condition.table, '1300000000001');
condition.columns = res.data.result;
condition.column = undefined;
condition.value = undefined;
};
//
const changeConditionColumn = async (condition: any) => {
//
const changeWhereColumn = async (condition: any) => {
//
var column = condition.columns.find((column: any) => column.tableName + '.' + column.dbColumnName == condition.column);
if (column) {
@ -228,95 +508,89 @@ const changeConditionColumn = async (condition: any) => {
}
};
//
const onColumnsChange = () => {
//
state.sqlString = '';
state.selectedColumnDetails = [];
//
if (state.selectedColumns.length <= 0 || state.dbColumns.length <= 0) return;
state.selectedColumns.forEach((colFullName: string) => {
// "."
const [tableName, dbColumnName] = colFullName.split('.');
// columns
if (tableName && dbColumnName) {
const matchedColumn = state.dbColumns.find((column: any) => column.tableName === tableName && column.dbColumnName === dbColumnName);
// selectedColumnDetails
if (matchedColumn) {
state.selectedColumnDetails.push(matchedColumn);
return;
}
}
});
};
//
const addCondition = () => {
state.queryConditions.push({
//
const addWhereConfig = () => {
state.whereConfigs.push({
table: '',
column: '',
operator: '=',
value: '',
columns: [],
dataType: '',
joinType: 'AND',
});
};
//
const removeCondition = (index: number) => {
state.queryConditions.splice(index, 1);
//
const removeWhereConfig = (index: number) => {
state.whereConfigs.splice(index, 1);
};
//
const clearCondition = () => {
state.queryConditions = [];
//
const clearWhereConfig = () => {
state.whereConfigs = [];
};
//
const addSortCondition = () => {
state.sortConditions.push({
//
const addSortConfig = () => {
state.orderConfigs.push({
table: '',
column: '',
sortOrder: 10,
sort: 10,
columns: [],
});
};
//
const removeSortCondition = (index: number) => {
state.sortConditions.splice(index, 1);
//
const removeSortConfig = (index: number) => {
state.orderConfigs.splice(index, 1);
};
//
const clearSortCondition = () => {
state.sortConditions = [];
//
const clearSortConfig = () => {
state.orderConfigs = [];
};
// SQL
// SQL
const generateSQL = async () => {
var queryJson = {
columns: state.selectedColumnDetails
.filter((col: any) => state.selectedTables.includes(col.tableName)) //
.map((col: any) => ({
table: col.tableName,
column: col.dbColumnName,
selectConfigs: state.selectConfigs
.filter((config: any) => state.dbConfigs.some((dbConfig: any) => dbConfig.selectedTables.includes(config.tableName))) //
.map((config: any) => ({
table: config.tableName,
column: config.dbColumnName,
})),
conditions: state.queryConditions.map((cond) => {
return {
table: cond.table,
column: cond.column.includes('.') ? cond.column.split('.')[1] : cond.column,
operator: cond.operator,
value: cond.value,
};
}),
orders: state.sortConditions.map((sortCond) => {
return {
table: sortCond.table,
column: sortCond.column.includes('.') ? sortCond.column.split('.')[1] : sortCond.column,
sort: sortCond.sortOrder,
};
}),
joinConfigs: state.joinConfigs
.filter((config: any) => config.leftTable != undefined && config.rightTable != undefined && config.tableColumns != undefined)
.map((config) => {
return {
joinType: config.joinType,
leftTable: config.leftTable,
rightTable: config.rightTable,
tableColumns: config.tableColumns,
};
}),
whereConfigs: state.whereConfigs
.filter((config: any) => config.table != undefined && config.column != undefined && config.value != undefined)
.map((config) => {
return {
table: config.table,
column: config.column.includes('.') ? config.column.split('.')[1] : config.column,
operator: config.operator,
value: config.value,
dataType: config.dataType,
joinType: config.joinType,
};
}),
orderConfigs: state.orderConfigs
.filter((config: any) => config.table != undefined && config.column != undefined)
.map((config) => {
return {
table: config.table,
column: config.column.includes('.') ? config.column.split('.')[1] : config.column,
sort: config.sort,
};
}),
};
var res = await getAPI(SysDatabaseApi).apiSysDatabaseGenerateSQLPost(queryJson);
state.sqlString = res.data.result ?? '';
@ -338,26 +612,12 @@ const generateSQL = async () => {
const executeSQL = async () => {
var queryJson = await generateSQL();
var res = await getAPI(SysDatabaseApi).apiSysDatabaseExecuteSQLPost(queryJson);
console.log(res.data.result);
state.queryResults[0].data = res.data.result ?? [];
state.queryResult = res.data.result ?? [];
//
xGrid.value?.loadData(state.queryResult);
};
// 便 Element Plus 使
const formattedTableData = computed(() => {
if (!state.queryResults || !state.queryResults[0] || !state.queryResults[0].data) {
return [];
}
//
return state.queryResults[0].data.map((row, rowIndex) => {
const formattedRow: Record<string, any> = {};
row.forEach((cell, cellIndex) => {
formattedRow[`col${cellIndex}`] = cell;
});
return formattedRow;
});
});
//
const openSchema = () => {
//
@ -374,18 +634,22 @@ const openSchema = () => {
try {
// JSON
const schemaData = JSON.parse(e.target?.result as string);
if (schemaData.selectedTable) {
state.selectedTables = schemaData.selectedTable;
if (schemaData.dbConfigs) {
state.dbConfigs = schemaData.dbConfigs;
}
if (schemaData.selectedColumns) {
state.selectedColumns = schemaData.selectedColumns;
if (schemaData.selectConfigs) {
state.selectConfigs = schemaData.selectConfigs;
}
if (schemaData.queryConditions) {
state.queryConditions = schemaData.queryConditions;
if (schemaData.whereConfigs) {
state.whereConfigs = schemaData.whereConfigs;
}
if (schemaData.sortConditions) {
state.sortConditions = schemaData.sortConditions;
if (schemaData.joinConfigs) {
state.joinConfigs = schemaData.joinConfigs;
}
if (schemaData.orderConfigs) {
state.orderConfigs = schemaData.orderConfigs;
}
ElMessage.success('自定义查询方案加载成功');
} catch (error) {
ElMessage.error('自定义查询方案文件格式错误');
@ -401,16 +665,17 @@ const openSchema = () => {
//
const saveSchema = () => {
if (state.queryConditions.length === 0) {
if (state.whereConfigs.length === 0) {
ElMessage.warning('请先进行配置');
return;
}
//
const schemaData = {
selectedTable: state.selectedTables,
selectedColumns: state.selectedColumns,
queryConditions: state.queryConditions,
sortConditions: state.sortConditions,
dbConfigs: state.dbConfigs, //
selectConfigs: state.selectConfigs, //
whereConfigs: state.whereConfigs, //
orderConfigs: state.orderConfigs, //
joinConfigs: state.joinConfigs, //
};
// JSON
@ -438,14 +703,74 @@ const saveSchema = () => {
</script>
<style lang="scss" scoped>
.conditions-container {
.config-container {
width: 100%;
padding: 0 10px 10px 10px;
}
.condition-item {
:deep(.el-card) {
height: 100%;
.el-card__body {
height: 100%;
overflow-y: auto;
}
}
.config-item {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.config-item-join {
display: flex;
flex-direction: column;
border-bottom: 1px dashed #c0c4cc;
padding: 0 5px;
margin-bottom: 10px;
.config-item-left {
display: flex;
margin-bottom: 10px;
}
// .config-item-right {
// width: 100%;
// }
.config-item-right-item {
display: flex;
margin-bottom: 5px;
}
}
.config-item-db {
display: flex;
// align-items: center;
.config-item-left {
display: flex;
margin-bottom: 10px;
align-items: center;
:deep(.el-select) {
width: calc(100% - 45px);
margin-left: 5px;
}
}
.config-item-right {
display: flex;
margin-bottom: 10px;
align-items: center;
:deep(.el-select) {
width: calc(100% - 45px);
margin-left: 5px;
}
}
.config-item-button {
display: flex;
margin-bottom: 10px;
align-items: center;
}
}
.config-item-db:last-child {
border-bottom: none;
margin-bottom: 0;
}
.config-item-join:last-child {
border-bottom: none;
margin-bottom: 0;
}
</style>