feat(control): add extended return search for tables and forms
This commit is contained in:
@@ -139,6 +139,10 @@ namespace Lskj.Control.Model
|
||||
/// <value>The text member.</value>
|
||||
public string TextMember { get; set; }
|
||||
/// <summary>
|
||||
/// 扩展返回字段映射,格式为“业务控件字段=返回数据字段”。
|
||||
/// </summary>
|
||||
public string ResultFields { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the source SQL.
|
||||
/// </summary>
|
||||
/// <value>The source SQL.</value>
|
||||
|
||||
@@ -493,21 +493,13 @@ namespace Lskj.Control.Model
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 快速搜索框返回id(字典模式)
|
||||
/// 模块选择返回ID-扩展
|
||||
/// </summary>
|
||||
public const int DictionarySearchBoxToId = 181;
|
||||
public const int LabModuleSelectReturnIdExtended = 180;
|
||||
/// <summary>
|
||||
/// 快速搜索框返回text(字典模式)
|
||||
/// 搜索返回ID-扩展
|
||||
/// </summary>
|
||||
public const int DictionarySearchBoxToText = 182;
|
||||
/// <summary>
|
||||
/// 快速搜索框返回id 带参数(字典模式)
|
||||
/// </summary>
|
||||
public const int DictionarySearchBoxToIdParam = 183;
|
||||
/// <summary>
|
||||
/// 快速搜索框返回text 带参数(字典模式)
|
||||
/// </summary>
|
||||
public const int DictionarySearchBoxToTextParam = 184;
|
||||
public const int LabSearchReturnIdExtended = 181;
|
||||
|
||||
/// <summary>
|
||||
/// <para>说明:是否为Value类型</para>
|
||||
@@ -628,6 +620,8 @@ namespace Lskj.Control.Model
|
||||
fieldType == LabPhone ||
|
||||
fieldType == LabSelectReturnIdNew ||
|
||||
fieldType == LabSelectReturnTextNew ||
|
||||
fieldType == LabModuleSelectReturnIdExtended ||
|
||||
fieldType == LabSearchReturnIdExtended ||
|
||||
fieldType == LabApiBtuton||
|
||||
fieldType == LabModuleAddRowsID ||
|
||||
fieldType == LabModuleAddRowsText;
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Lskj.Control.Model
|
||||
{
|
||||
internal sealed class ExtendedReturnFieldMapping
|
||||
{
|
||||
public ExtendedReturnFieldMapping(string targetField, string sourceField)
|
||||
{
|
||||
TargetField = targetField;
|
||||
SourceField = sourceField;
|
||||
}
|
||||
|
||||
public string TargetField { get; private set; }
|
||||
|
||||
public string SourceField { get; private set; }
|
||||
}
|
||||
|
||||
internal static class ExtendedReturnSupport
|
||||
{
|
||||
internal const string SearchAlias = "LS_ExtendedLookup";
|
||||
internal const string SearchParameterName = "@lookupKeyword";
|
||||
|
||||
public static IList<ExtendedReturnFieldMapping> ParseResultFields(string resultFields)
|
||||
{
|
||||
List<ExtendedReturnFieldMapping> mappings = new List<ExtendedReturnFieldMapping>();
|
||||
if (string.IsNullOrWhiteSpace(resultFields)) return mappings;
|
||||
|
||||
HashSet<string> targetFields = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
string[] items = resultFields.Split(new char[] { ';', ';' });
|
||||
foreach (string rawItem in items)
|
||||
{
|
||||
string item = (rawItem ?? string.Empty).Trim();
|
||||
if (item.Length == 0) continue;
|
||||
|
||||
int separatorIndex = item.IndexOf('=');
|
||||
if (separatorIndex <= 0 || separatorIndex == item.Length - 1 || item.IndexOf('=', separatorIndex + 1) >= 0)
|
||||
{
|
||||
throw new FormatException(string.Format("返回字段配置“{0}”格式错误,应为“业务表字段=返回数据字段”。", item));
|
||||
}
|
||||
|
||||
string targetField = item.Substring(0, separatorIndex).Trim();
|
||||
string sourceField = item.Substring(separatorIndex + 1).Trim();
|
||||
if (targetField.Length == 0 || sourceField.Length == 0)
|
||||
{
|
||||
throw new FormatException(string.Format("返回字段配置“{0}”存在空字段名。", item));
|
||||
}
|
||||
if (!targetFields.Add(targetField))
|
||||
{
|
||||
throw new FormatException(string.Format("返回字段配置中的业务表字段“{0}”重复。", targetField));
|
||||
}
|
||||
|
||||
mappings.Add(new ExtendedReturnFieldMapping(targetField, sourceField));
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public static string FindTargetField(IEnumerable<ExtendedReturnFieldMapping> mappings, string sourceField)
|
||||
{
|
||||
if (mappings == null || string.IsNullOrWhiteSpace(sourceField)) return string.Empty;
|
||||
|
||||
foreach (ExtendedReturnFieldMapping mapping in mappings)
|
||||
{
|
||||
if (string.Equals(mapping.SourceField, sourceField, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return mapping.TargetField;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string BuildStructureSql(string sourceSql)
|
||||
{
|
||||
return string.Format("select top 0 * from ({0}) as [{1}]", NormalizeSourceSql(sourceSql), SearchAlias);
|
||||
}
|
||||
|
||||
public static string BuildSearchSql(string sourceSql, DataColumnCollection columns, int maxRows)
|
||||
{
|
||||
if (columns == null || columns.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("搜索 SQL 没有返回任何可查询列。");
|
||||
}
|
||||
if (maxRows <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("maxRows", "最大返回行数必须大于零。");
|
||||
}
|
||||
|
||||
string alias = QuoteIdentifier(SearchAlias);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.AppendFormat("select top ({0}) * from ({1}) as {2} where ", maxRows, NormalizeSourceSql(sourceSql), alias);
|
||||
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
{
|
||||
if (i > 0) builder.Append(" or ");
|
||||
builder.Append("convert(nvarchar(4000), ");
|
||||
builder.Append(alias);
|
||||
builder.Append('.');
|
||||
builder.Append(QuoteIdentifier(columns[i].ColumnName));
|
||||
builder.Append(") like ");
|
||||
builder.Append(SearchParameterName);
|
||||
builder.Append(" escape N'\\'");
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static string BuildLikeParameterValue(string keyword)
|
||||
{
|
||||
string value = keyword ?? string.Empty;
|
||||
value = value.Replace("\\", "\\\\");
|
||||
value = value.Replace("%", "\\%");
|
||||
value = value.Replace("_", "\\_");
|
||||
value = value.Replace("[", "\\[");
|
||||
return "%" + value + "%";
|
||||
}
|
||||
|
||||
private static string NormalizeSourceSql(string sourceSql)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sourceSql))
|
||||
{
|
||||
throw new InvalidOperationException("搜索数据源 SQL 不能为空。");
|
||||
}
|
||||
|
||||
string normalized = sourceSql.Trim();
|
||||
if (normalized.EndsWith(";", StringComparison.Ordinal))
|
||||
{
|
||||
normalized = normalized.Substring(0, normalized.Length - 1).TrimEnd();
|
||||
}
|
||||
if (normalized.IndexOf(';') >= 0)
|
||||
{
|
||||
throw new InvalidOperationException("扩展搜索只支持一条可组合的 SELECT 语句。");
|
||||
}
|
||||
if (!Regex.IsMatch(normalized, @"^select\b", RegexOptions.IgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException("扩展搜索数据源必须是可作为派生表使用的 SELECT 语句。");
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static string QuoteIdentifier(string identifier)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(identifier))
|
||||
{
|
||||
throw new InvalidOperationException("搜索 SQL 返回了空列名。");
|
||||
}
|
||||
return "[" + identifier.Replace("]", "]]") + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,7 @@ namespace Lskj.Control.Model
|
||||
private bool _frozenFlag;
|
||||
private int _lookUpWidth;
|
||||
private string _lookUpFieldsWidth;
|
||||
private string _resultFields;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -326,6 +327,10 @@ namespace Lskj.Control.Model
|
||||
/// </summary>
|
||||
public string LookUpFieldsWidth { get { return this._lookUpFieldsWidth; } }
|
||||
/// <summary>
|
||||
/// 扩展选择结果映射,格式:业务表字段=返回数据字段;业务表字段1=返回数据字段1。
|
||||
/// </summary>
|
||||
public string ResultFields { get { return this._resultFields; } }
|
||||
/// <summary>
|
||||
/// 动态列
|
||||
/// </summary>
|
||||
public bool isDynamic;
|
||||
@@ -459,6 +464,7 @@ namespace Lskj.Control.Model
|
||||
}
|
||||
|
||||
this.addModuleResult = item.Table.Columns.Contains("addModuleResult") ? item["addModuleResult"] + "" : string.Empty;
|
||||
this._resultFields = item.Table.Columns.Contains("resultfields") ? item["resultfields"] + "" : string.Empty;
|
||||
this.addModuleld = item.Table.Columns.Contains("addModuleId") ? item["addModuleId"] + "" : string.Empty;
|
||||
this.unionCompare = item.Table.Columns.Contains("UnionCompare") ? item["UnionCompare"] + "" : string.Empty;
|
||||
this.isExportColumns = item.Table.Columns.Contains("isExportColumns") && !string.IsNullOrWhiteSpace(item["isExportColumns"] + "") ? "1".Equals(item["isExportColumns"] + "") : false;
|
||||
|
||||
@@ -136,6 +136,11 @@ namespace Lskj.Control.Model
|
||||
/// 下拉框、搜索框控件对象
|
||||
/// </summary>
|
||||
public List<ControlModel> mControlList = new List<ControlModel>();
|
||||
private readonly List<LabelExtendedReturnSearchEdit> mExtendedReturnSearchControls =
|
||||
new List<LabelExtendedReturnSearchEdit>();
|
||||
private readonly List<KeyValuePair<object, EventArgs>> mExtendedReturnPendingChanges =
|
||||
new List<KeyValuePair<object, EventArgs>>();
|
||||
private int mExtendedReturnBatchDepth;
|
||||
/// <summary>
|
||||
/// 数据源任务对象
|
||||
/// </summary>
|
||||
@@ -2253,6 +2258,10 @@ namespace Lskj.Control.Model
|
||||
LabelMultiAutoTextEdit4 moduleReturnsIdNew = ctr as LabelMultiAutoTextEdit4;
|
||||
value = ControlType.LabSelectReturnIdNew == model.FieldType ? moduleReturnsIdNew.EditValue : moduleReturnsIdNew.EditText;
|
||||
break;
|
||||
case ControlType.LabSearchReturnIdExtended:
|
||||
LabelExtendedReturnSearchEdit extendedReturnSearch = ctr as LabelExtendedReturnSearchEdit;
|
||||
value = extendedReturnSearch == null ? string.Empty : extendedReturnSearch.EditValue;
|
||||
break;
|
||||
case ControlType.DynamicallyGeneratedSql:
|
||||
LabelMultiAutoTextEdit3 moduleSqlGenerated = ctr as LabelMultiAutoTextEdit3;
|
||||
value = moduleSqlGenerated.EditText;
|
||||
@@ -2679,6 +2688,7 @@ namespace Lskj.Control.Model
|
||||
|
||||
}
|
||||
}
|
||||
RefreshExtendedReturnDisplayControls();
|
||||
foreach (ControlModel model in this.ControlModels)
|
||||
{
|
||||
BaseUserControl control = FindControl(model);
|
||||
@@ -3710,6 +3720,14 @@ namespace Lskj.Control.Model
|
||||
}
|
||||
pictureEdit.TextEdit.Image = image;
|
||||
}
|
||||
else if (baseControl is LabelExtendedReturnSearchEdit)
|
||||
{
|
||||
LabelExtendedReturnSearchEdit extendedReturnSearch = baseControl as LabelExtendedReturnSearchEdit;
|
||||
extendedReturnSearch.EditValue = controlValue == null || controlValue == DBNull.Value
|
||||
? string.Empty
|
||||
: controlValue + "";
|
||||
baseControl.Model.Text = extendedReturnSearch.EditValue;
|
||||
}
|
||||
else if (baseControl.Model.FieldType == 7 && !"****".Equals(controlValue))
|
||||
{
|
||||
string dfmt = baseControl.Model.DataFormat;
|
||||
@@ -3750,6 +3768,113 @@ namespace Lskj.Control.Model
|
||||
//baseControl.Parent.Focus();
|
||||
}
|
||||
}
|
||||
RefreshExtendedReturnDisplayControls();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户从扩展搜索弹窗选择或清空时,批量写入所有映射字段。
|
||||
/// 中间值改变事件会延迟到全部字段提交成功后执行,且不改变原始值基线。
|
||||
/// </summary>
|
||||
public void ApplyExtendedReturnValues(IList<KeyValuePair<string, object>> values)
|
||||
{
|
||||
if (values == null || values.Count == 0) return;
|
||||
if (mExtendedReturnBatchDepth > 0)
|
||||
{
|
||||
throw new InvalidOperationException("扩展返回字段正在批量赋值,不能重复执行。");
|
||||
}
|
||||
|
||||
List<KeyValuePair<string, object>> normalizedValues =
|
||||
new List<KeyValuePair<string, object>>();
|
||||
Dictionary<string, string> originalValues =
|
||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
Dictionary<BaseUserControl, string> originalModelTexts =
|
||||
new Dictionary<BaseUserControl, string>();
|
||||
HashSet<string> targetFields = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (KeyValuePair<string, object> value in values)
|
||||
{
|
||||
string targetField = (value.Key ?? string.Empty).Trim();
|
||||
if (targetField.Length == 0)
|
||||
{
|
||||
throw new InvalidOperationException("扩展返回字段名称不能为空。");
|
||||
}
|
||||
if (!targetFields.Add(targetField))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
string.Format("扩展返回字段“{0}”重复。", targetField));
|
||||
}
|
||||
|
||||
BaseUserControl targetControl = FindControl(targetField);
|
||||
if (targetControl == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
string.Format("MyControl 中不存在业务控件“{0}”。", targetField));
|
||||
}
|
||||
|
||||
object normalizedValue = value.Value == null || value.Value == DBNull.Value
|
||||
? string.Empty
|
||||
: value.Value;
|
||||
normalizedValues.Add(new KeyValuePair<string, object>(targetField, normalizedValue));
|
||||
originalValues[targetField] = GetControlValue(targetField);
|
||||
originalModelTexts[targetControl] = targetControl.Model == null
|
||||
? null
|
||||
: targetControl.Model.Text;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
mExtendedReturnPendingChanges.Clear();
|
||||
mExtendedReturnBatchDepth++;
|
||||
try
|
||||
{
|
||||
foreach (KeyValuePair<string, object> value in normalizedValues)
|
||||
{
|
||||
SetControlValue(value.Key, value.Value);
|
||||
}
|
||||
RestoreExtendedReturnModelTexts(originalModelTexts);
|
||||
RefreshExtendedReturnDisplayControls(true);
|
||||
success = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
foreach (KeyValuePair<string, string> originalValue in originalValues)
|
||||
{
|
||||
SetControlValue(originalValue.Key, originalValue.Value);
|
||||
}
|
||||
RestoreExtendedReturnModelTexts(originalModelTexts);
|
||||
RefreshExtendedReturnDisplayControls(true);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
mExtendedReturnBatchDepth--;
|
||||
if (!success) mExtendedReturnPendingChanges.Clear();
|
||||
}
|
||||
|
||||
FlushExtendedReturnPendingChanges();
|
||||
}
|
||||
|
||||
private static void RestoreExtendedReturnModelTexts(
|
||||
IDictionary<BaseUserControl, string> originalModelTexts)
|
||||
{
|
||||
foreach (KeyValuePair<BaseUserControl, string> item in originalModelTexts)
|
||||
{
|
||||
if (item.Key != null && item.Key.Model != null)
|
||||
{
|
||||
item.Key.Model.Text = item.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshExtendedReturnDisplayControls(bool force = false)
|
||||
{
|
||||
if (mExtendedReturnBatchDepth > 0 && !force) return;
|
||||
foreach (LabelExtendedReturnSearchEdit control in mExtendedReturnSearchControls)
|
||||
{
|
||||
if (control != null && !control.IsDisposed && control.Model != null)
|
||||
{
|
||||
control.RefreshDisplayText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -4152,6 +4277,7 @@ namespace Lskj.Control.Model
|
||||
model.Sign = item.Table.Columns.Contains("TM_tagID") && !string.IsNullOrEmpty(item["TM_tagID"] + "") ? Convert.ToInt32(item["TM_tagID"] + "") : 0;
|
||||
model.TextMember = item["lookupResult"] + "";
|
||||
model.ValueMember = item["lookupKeyField"] + "";
|
||||
model.ResultFields = item.Table.Columns.Contains("resultfields") ? item["resultfields"] + "" : string.Empty;
|
||||
|
||||
if (!dataCaches.GetValue(item, "LookupSql", out string lookupSql))
|
||||
{
|
||||
@@ -4239,28 +4365,6 @@ namespace Lskj.Control.Model
|
||||
model.FieldType = 161;
|
||||
model.IsRadio = true;
|
||||
}
|
||||
//快速搜索框在控件中转成普通搜索框
|
||||
if (model.FieldType == 181 || model.FieldType == 182 || model.FieldType == 183 || model.FieldType == 184)
|
||||
{
|
||||
switch (model.FieldType)
|
||||
{
|
||||
case ControlType.DictionarySearchBoxToId:
|
||||
model.FieldType = 5;
|
||||
break;
|
||||
case ControlType.DictionarySearchBoxToText:
|
||||
model.FieldType = 6;
|
||||
break;
|
||||
case ControlType.DictionarySearchBoxToIdParam:
|
||||
model.FieldType = 15;
|
||||
break;
|
||||
case ControlType.DictionarySearchBoxToTextParam:
|
||||
model.FieldType = 16;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
/// <summary>
|
||||
@@ -4830,6 +4934,18 @@ namespace Lskj.Control.Model
|
||||
if (!isLoadBorder) moduleReturnsIdNew.TextEdit.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder;
|
||||
baseControl = moduleReturnsIdNew;
|
||||
break;
|
||||
case ControlType.LabSearchReturnIdExtended:
|
||||
LabelExtendedReturnSearchEdit extendedReturnSearch = new LabelExtendedReturnSearchEdit();
|
||||
extendedReturnSearch.ControlObj = this;
|
||||
mExtendedReturnSearchControls.Add(extendedReturnSearch);
|
||||
extendedReturnSearch.TextEdit.KeyDown += new KeyEventHandler(OnTextEditKeyDown);
|
||||
extendedReturnSearch.TextEdit.TextChanged += new EventHandler(OnTextEditTextChanged);
|
||||
if (!isLoadBorder)
|
||||
{
|
||||
extendedReturnSearch.TextEdit.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder;
|
||||
}
|
||||
baseControl = extendedReturnSearch;
|
||||
break;
|
||||
case ControlType.DynamicallyGeneratedSql:
|
||||
LabelMultiAutoTextEdit3 moduleSqlGenerated = new LabelMultiAutoTextEdit3();
|
||||
// moduleSqlGenerated.SetDataSource();
|
||||
@@ -5884,6 +6000,49 @@ namespace Lskj.Control.Model
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
|
||||
protected void OnTextEditTextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (mExtendedReturnBatchDepth > 0)
|
||||
{
|
||||
QueueExtendedReturnPendingChange(sender, e);
|
||||
return;
|
||||
}
|
||||
ProcessTextEditTextChanged(sender, e);
|
||||
}
|
||||
|
||||
private void QueueExtendedReturnPendingChange(object sender, EventArgs e)
|
||||
{
|
||||
System.Windows.Forms.Control changedControl = sender as System.Windows.Forms.Control;
|
||||
BaseUserControl baseControl = changedControl == null
|
||||
? null
|
||||
: changedControl.ToBaseUserControl();
|
||||
foreach (KeyValuePair<object, EventArgs> pendingChange in mExtendedReturnPendingChanges)
|
||||
{
|
||||
System.Windows.Forms.Control pendingControl =
|
||||
pendingChange.Key as System.Windows.Forms.Control;
|
||||
BaseUserControl pendingBaseControl = pendingControl == null
|
||||
? null
|
||||
: pendingControl.ToBaseUserControl();
|
||||
if (baseControl != null && object.ReferenceEquals(baseControl, pendingBaseControl))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
mExtendedReturnPendingChanges.Add(
|
||||
new KeyValuePair<object, EventArgs>(sender, e ?? EventArgs.Empty));
|
||||
}
|
||||
|
||||
private void FlushExtendedReturnPendingChanges()
|
||||
{
|
||||
KeyValuePair<object, EventArgs>[] pendingChanges =
|
||||
mExtendedReturnPendingChanges.ToArray();
|
||||
mExtendedReturnPendingChanges.Clear();
|
||||
foreach (KeyValuePair<object, EventArgs> pendingChange in pendingChanges)
|
||||
{
|
||||
ProcessTextEditTextChanged(pendingChange.Key, pendingChange.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessTextEditTextChanged(object sender, EventArgs e)
|
||||
{
|
||||
BaseUserControl baseControl = (sender as System.Windows.Forms.Control).ToBaseUserControl();
|
||||
this.SetUnionAncCalcControl(baseControl);
|
||||
|
||||
Reference in New Issue
Block a user