mirror of
https://gitee.com/dotnetchina/MiniExcel.git
synced 2024-11-29 18:38:08 +08:00
Add supprt for if/elseif/else statements inside cell (#487)
* add support for grouped cells * fix for rows after endgroup * minor fix for text after grouping * fix unit tests * fix unit tests and docs * minor fix * Add support to vertical merge cells * minor fixes in merge cells * minor fix * fix complex scenario * finalize changes * Add support for if/elseif/else statements
This commit is contained in:
parent
1bd853affe
commit
0559847ecc
31
README.md
31
README.md
@ -808,8 +808,35 @@ After
|
|||||||
|
|
||||||
![without_group_after](https://user-images.githubusercontent.com/38832863/218646974-4a3c0e07-7c66-4088-ad07-b4ad3695b7e1.PNG)
|
![without_group_after](https://user-images.githubusercontent.com/38832863/218646974-4a3c0e07-7c66-4088-ad07-b4ad3695b7e1.PNG)
|
||||||
|
|
||||||
|
#### 8. If/ElseIf/Else Statements inside cell
|
||||||
|
|
||||||
#### 8. DataTable as parameter
|
Rules:
|
||||||
|
1. Supports DateTime, Double, Int with ==, !=, >, >=, <, <= operators.
|
||||||
|
2. Supports String with ==, != operators.
|
||||||
|
3. Each statement should be new line.
|
||||||
|
4. Single space should be added before and after operators.
|
||||||
|
5. There shouldn't be new line inside of statements.
|
||||||
|
6. Cell should be in exact format as below.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
@if(name == Jack)
|
||||||
|
{{employees.name}}
|
||||||
|
@elseif(name == Neo)
|
||||||
|
Test {{employees.name}}
|
||||||
|
@else
|
||||||
|
{{employees.department}}
|
||||||
|
@endif
|
||||||
|
```
|
||||||
|
|
||||||
|
Before
|
||||||
|
|
||||||
|
![if_before](https://user-images.githubusercontent.com/38832863/235360606-ca654769-ff55-4f5b-98d2-d2ec0edb8173.PNG)
|
||||||
|
|
||||||
|
After
|
||||||
|
|
||||||
|
![if_after](https://user-images.githubusercontent.com/38832863/235360609-869bb960-d63d-45ae-8d64-9e8b0d0ab658.PNG)
|
||||||
|
|
||||||
|
#### 9. DataTable as parameter
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var managers = new DataTable();
|
var managers = new DataTable();
|
||||||
@ -828,7 +855,7 @@ MiniExcel.SaveAsByTemplate(path, templatePath, value);
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### 9. Other
|
#### 10. Other
|
||||||
|
|
||||||
##### 1. Checking template parameter key
|
##### 1. Checking template parameter key
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 1.30.3
|
||||||
|
- [New] support if/else if/else statements inside cell (via @eynarhaji)
|
||||||
|
|
||||||
### 1.30.2
|
### 1.30.2
|
||||||
- [New] support grouped rows (via @eynarhaji)
|
- [New] support grouped rows (via @eynarhaji)
|
||||||
|
BIN
samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx
Normal file
BIN
samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx
Normal file
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
|
<TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
|
||||||
<Version>1.30.2</Version>
|
<Version>1.30.3</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>MiniExcel</AssemblyName>
|
<AssemblyName>MiniExcel</AssemblyName>
|
||||||
|
@ -351,9 +351,51 @@ namespace MiniExcelLibs.OpenXml
|
|||||||
.Replace($"{{{{$rowindex}}}}", newRowIndex.ToString())
|
.Replace($"{{{{$rowindex}}}}", newRowIndex.ToString())
|
||||||
.AppendFormat(@"</{0}>", row.Name);
|
.AppendFormat(@"</{0}>", row.Name);
|
||||||
|
|
||||||
|
var rowXmlString = rowXml.ToString();
|
||||||
|
var extract = "";
|
||||||
|
var newCellValue = "";
|
||||||
|
|
||||||
|
var ifIndex = rowXmlString.IndexOf("@if", StringComparison.Ordinal);
|
||||||
|
var endifIndex = rowXmlString.IndexOf("@endif", StringComparison.Ordinal);
|
||||||
|
|
||||||
|
if (ifIndex != -1 && endifIndex != -1)
|
||||||
|
{
|
||||||
|
extract = rowXmlString.Substring(ifIndex, endifIndex - ifIndex + 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = extract.Split('\n');
|
||||||
|
|
||||||
if (rowInfo.IsDictionary)
|
if (rowInfo.IsDictionary)
|
||||||
{
|
{
|
||||||
var dic = item as IDictionary<string, object>;
|
var dic = item as IDictionary<string, object>;
|
||||||
|
|
||||||
|
for(var i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
if(lines[i].Contains("@if") || lines[i].Contains("@elseif"))
|
||||||
|
{
|
||||||
|
var newLines = lines[i].Replace("@elseif(", "").Replace("@if(", "").Replace(")", "").Split(' ');
|
||||||
|
|
||||||
|
var value = dic[newLines[0]];
|
||||||
|
var evaluation = EvaluateStatement(value, newLines[1], newLines[2]);
|
||||||
|
|
||||||
|
if (evaluation)
|
||||||
|
{
|
||||||
|
newCellValue += lines[i + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(lines[i].Contains("@else"))
|
||||||
|
{
|
||||||
|
newCellValue += lines[i + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(newCellValue))
|
||||||
|
{
|
||||||
|
rowXml.Replace(extract, newCellValue);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var propInfo in rowInfo.PropsMap)
|
foreach (var propInfo in rowInfo.PropsMap)
|
||||||
{
|
{
|
||||||
var key = $"{{{{{rowInfo.IEnumerablePropName}.{propInfo.Key}}}}}";
|
var key = $"{{{{{rowInfo.IEnumerablePropName}.{propInfo.Key}}}}}";
|
||||||
@ -396,6 +438,34 @@ namespace MiniExcelLibs.OpenXml
|
|||||||
else if (rowInfo.IsDataTable)
|
else if (rowInfo.IsDataTable)
|
||||||
{
|
{
|
||||||
var datarow = item as DataRow;
|
var datarow = item as DataRow;
|
||||||
|
|
||||||
|
for(var i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
if(lines[i].Contains("@if") || lines[i].Contains("@elseif"))
|
||||||
|
{
|
||||||
|
var newLines = lines[i].Replace("@elseif(", "").Replace("@if(", "").Replace(")", "").Split(' ');
|
||||||
|
|
||||||
|
var value = datarow[newLines[0]];
|
||||||
|
var evaluation = EvaluateStatement(value, newLines[1], newLines[2]);
|
||||||
|
|
||||||
|
if (evaluation)
|
||||||
|
{
|
||||||
|
newCellValue += lines[i + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(lines[i].Contains("@else"))
|
||||||
|
{
|
||||||
|
newCellValue += lines[i + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(newCellValue))
|
||||||
|
{
|
||||||
|
rowXml.Replace(extract, newCellValue);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var propInfo in rowInfo.PropsMap)
|
foreach (var propInfo in rowInfo.PropsMap)
|
||||||
{
|
{
|
||||||
var key = $"{{{{{rowInfo.IEnumerablePropName}.{propInfo.Key}}}}}";
|
var key = $"{{{{{rowInfo.IEnumerablePropName}.{propInfo.Key}}}}}";
|
||||||
@ -437,6 +507,33 @@ namespace MiniExcelLibs.OpenXml
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
for(var i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
if(lines[i].Contains("@if") || lines[i].Contains("@elseif"))
|
||||||
|
{
|
||||||
|
var newLines = lines[i].Replace("@elseif(", "").Replace("@if(", "").Replace(")", "").Split(' ');
|
||||||
|
|
||||||
|
var value = rowInfo.PropsMap[newLines[0]].PropertyInfo.GetValue(item);
|
||||||
|
var evaluation = EvaluateStatement(value, newLines[1], newLines[2]);
|
||||||
|
|
||||||
|
if (evaluation)
|
||||||
|
{
|
||||||
|
newCellValue += lines[i + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(lines[i].Contains("@else"))
|
||||||
|
{
|
||||||
|
newCellValue += lines[i + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(newCellValue))
|
||||||
|
{
|
||||||
|
rowXml.Replace(extract, newCellValue);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var propInfo in rowInfo.PropsMap)
|
foreach (var propInfo in rowInfo.PropsMap)
|
||||||
{
|
{
|
||||||
var prop = propInfo.Value.PropertyInfo;
|
var prop = propInfo.Value.PropertyInfo;
|
||||||
@ -900,5 +997,100 @@ namespace MiniExcelLibs.OpenXml
|
|||||||
dimension.SetAttribute("ref", $"A1:{letter}{digit + maxRowIndexDiff}");
|
dimension.SetAttribute("ref", $"A1:{letter}{digit + maxRowIndexDiff}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool EvaluateStatement(object tagValue, string comparisonOperator, string value)
|
||||||
|
{
|
||||||
|
var checkStatement = false;
|
||||||
|
|
||||||
|
switch (tagValue)
|
||||||
|
{
|
||||||
|
case double dtg when double.TryParse(value, out var doubleNumber):
|
||||||
|
switch (comparisonOperator)
|
||||||
|
{
|
||||||
|
case "==":
|
||||||
|
checkStatement = dtg.Equals(doubleNumber);
|
||||||
|
break;
|
||||||
|
case "!=":
|
||||||
|
checkStatement = !dtg.Equals(doubleNumber);
|
||||||
|
break;
|
||||||
|
case ">":
|
||||||
|
checkStatement = dtg > doubleNumber;
|
||||||
|
break;
|
||||||
|
case "<":
|
||||||
|
checkStatement = dtg < doubleNumber;
|
||||||
|
break;
|
||||||
|
case ">=":
|
||||||
|
checkStatement = dtg >= doubleNumber;
|
||||||
|
break;
|
||||||
|
case "<=":
|
||||||
|
checkStatement = dtg <= doubleNumber;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case int itg when int.TryParse(value, out var intNumber):
|
||||||
|
switch (comparisonOperator)
|
||||||
|
{
|
||||||
|
case "==":
|
||||||
|
checkStatement = itg.Equals(intNumber);
|
||||||
|
break;
|
||||||
|
case "!=":
|
||||||
|
checkStatement = !itg.Equals(intNumber);
|
||||||
|
break;
|
||||||
|
case ">":
|
||||||
|
checkStatement = itg > intNumber;
|
||||||
|
break;
|
||||||
|
case "<":
|
||||||
|
checkStatement = itg < intNumber;
|
||||||
|
break;
|
||||||
|
case ">=":
|
||||||
|
checkStatement = itg >= intNumber;
|
||||||
|
break;
|
||||||
|
case "<=":
|
||||||
|
checkStatement = itg <= intNumber;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case DateTime dttg when DateTime.TryParse(value, out var date):
|
||||||
|
switch (comparisonOperator)
|
||||||
|
{
|
||||||
|
case "==":
|
||||||
|
checkStatement = dttg.Equals(date);
|
||||||
|
break;
|
||||||
|
case "!=":
|
||||||
|
checkStatement = !dttg.Equals(date);
|
||||||
|
break;
|
||||||
|
case ">":
|
||||||
|
checkStatement = dttg > date;
|
||||||
|
break;
|
||||||
|
case "<":
|
||||||
|
checkStatement = dttg < date;
|
||||||
|
break;
|
||||||
|
case ">=":
|
||||||
|
checkStatement = dttg >= date;
|
||||||
|
break;
|
||||||
|
case "<=":
|
||||||
|
checkStatement = dttg <= date;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case string stg:
|
||||||
|
switch (comparisonOperator)
|
||||||
|
{
|
||||||
|
case "==":
|
||||||
|
checkStatement = stg == value;
|
||||||
|
break;
|
||||||
|
case "!=":
|
||||||
|
checkStatement = stg != value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkStatement;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -693,6 +693,80 @@ namespace MiniExcelTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TestIEnumerableConditional()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx");
|
||||||
|
var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx";
|
||||||
|
|
||||||
|
//1. By POCO
|
||||||
|
var value = new
|
||||||
|
{
|
||||||
|
employees = new[] {
|
||||||
|
new {name="Jack",department="HR"},
|
||||||
|
new {name="Lisa",department="HR"},
|
||||||
|
new {name="John",department="HR"},
|
||||||
|
new {name="Mike",department="IT"},
|
||||||
|
new {name="Neo",department="IT"},
|
||||||
|
new {name="Loan",department="IT"}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value);
|
||||||
|
|
||||||
|
var demension = Helpers.GetFirstSheetDimensionRefValue(path);
|
||||||
|
Assert.Equal("A1:B18", demension);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx");
|
||||||
|
var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx";
|
||||||
|
|
||||||
|
//2. By Dictionary
|
||||||
|
var value = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
["employees"] = new[] {
|
||||||
|
new {name="Jack",department="HR"},
|
||||||
|
new {name="Jack",department="HR"},
|
||||||
|
new {name="John",department="HR"},
|
||||||
|
new {name="John",department="IT"},
|
||||||
|
new {name="Neo",department="IT"},
|
||||||
|
new {name="Loan",department="IT"}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value);
|
||||||
|
|
||||||
|
var demension = Helpers.GetFirstSheetDimensionRefValue(path);
|
||||||
|
Assert.Equal("A1:B18", demension);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx");
|
||||||
|
var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFillConditional.xlsx";
|
||||||
|
|
||||||
|
//3. By DataTable
|
||||||
|
var dt = new DataTable();
|
||||||
|
{
|
||||||
|
dt.Columns.Add("name");
|
||||||
|
dt.Columns.Add("department");
|
||||||
|
dt.Rows.Add("Jack", "HR");
|
||||||
|
dt.Rows.Add("Lisa", "HR");
|
||||||
|
dt.Rows.Add("John", "HR");
|
||||||
|
dt.Rows.Add("Mike", "IT");
|
||||||
|
dt.Rows.Add("Neo", "IT");
|
||||||
|
dt.Rows.Add("Loan", "IT");
|
||||||
|
}
|
||||||
|
var value = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
["employees"] = dt
|
||||||
|
};
|
||||||
|
await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value);
|
||||||
|
|
||||||
|
var demension = Helpers.GetFirstSheetDimensionRefValue(path);
|
||||||
|
Assert.Equal("A1:B18", demension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TemplateTest()
|
public async Task TemplateTest()
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user