【特性】新增 xlsx 格式导出

【修复】修复 lua 导出格式中未对"转义的bug
main
walon 2021-10-26 17:48:02 +08:00
parent 050fbc347e
commit 881bd3aecc
17 changed files with 483 additions and 44 deletions

View File

@ -14,6 +14,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="ExcelDataReader" Version="3.6.0" /> <PackageReference Include="ExcelDataReader" Version="3.6.0" />
<PackageReference Include="NeoLua" Version="1.3.13" /> <PackageReference Include="NeoLua" Version="1.3.13" />
<PackageReference Include="SpreadsheetLight" Version="3.5.0" />
<PackageReference Include="Ude.NetStandard" Version="1.2.0" /> <PackageReference Include="Ude.NetStandard" Version="1.2.0" />
<PackageReference Include="YamlDotNet" Version="11.2.1" /> <PackageReference Include="YamlDotNet" Version="11.2.1" />
</ItemGroup> </ItemGroup>

View File

@ -2,16 +2,15 @@
using Luban.Job.Cfg.DataSources.Excel; using Luban.Job.Cfg.DataSources.Excel;
using Luban.Job.Cfg.DataVisitors; using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs; using Luban.Job.Cfg.Defs;
using Microsoft.Office.Interop.Excel;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace LubanAssistant namespace Luban.Job.Cfg.DataConverts
{ {
class FillSheetVisitor : IDataFuncVisitor<Title, int> public class FillSheetVisitor : IDataFuncVisitor<Title, int>
{ {
private readonly List<object[]> _cells; private readonly List<object[]> _cells;

View File

@ -0,0 +1,298 @@
using Luban.Job.Cfg.DataSources.Excel;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.TypeVisitors;
using Luban.Job.Common.Types;
using Luban.Job.Common.TypeVisitors;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.DataConverts
{
class TitleCreator : ITypeActionVisitor<Title, int>
{
public static TitleCreator Ins { get; } = new();
public Title CreateTitle(DefTable table)
{
TBean type = table.ValueTType;
//if (bean.IsDynamic)
//{
// throw new Exception($"bean:{bean.Bean.FullName} 是多态bean暂不支持");
//}
var title = new Title()
{
Root = true,
Name = "__root__",
Tags = new Dictionary<string, string>(),
FromIndex = 1,
};
int lastColumn = 0;
if (type.IsDynamic)
{
title.AddSubTitle(new Title()
{
Name = "__type__",
FromIndex = lastColumn + 1,
ToIndex = lastColumn + 1,
Tags = new Dictionary<string, string>(),
});
++lastColumn;
}
var fields = type.IsDynamic ? type.Bean.HierarchyNotAbstractChildren.SelectMany(c => c.HierarchyFields) : type.Bean.HierarchyFields;
foreach (var f in fields)
{
if (title.SubTitles.ContainsKey(f.Name))
{
continue;
}
int startColumn = lastColumn + 1;
var subTitle = new Title()
{
Name = f.Name,
FromIndex = startColumn,
ToIndex = startColumn,
Tags = new Dictionary<string, string>(),
};
if (f.CType.Tags.TryGetValue("sep", out var sep))
{
subTitle.Tags.Add("sep", sep);
}
if (f.CType.Tags.TryGetValue("multi_rows", out var multiRows))
{
subTitle.Tags.Add("multiRows", multiRows);
}
f.CType.Apply(this, subTitle, startColumn);
lastColumn = subTitle.ToIndex;
title.AddSubTitle(subTitle);
}
title.ToIndex = Math.Max(lastColumn, 1);
if (table.IsMapTable)
{
title.SubTitles[table.IndexField.Name].Tags.TryAdd("non_empty", "1");
}
title.Init();
return title;
}
public void Accept(TBool type, Title title, int column)
{
}
public void Accept(TByte type, Title title, int column)
{
}
public void Accept(TShort type, Title title, int column)
{
}
public void Accept(TFshort type, Title title, int column)
{
}
public void Accept(TInt type, Title title, int column)
{
}
public void Accept(TFint type, Title title, int column)
{
}
public void Accept(TLong type, Title title, int column)
{
}
public void Accept(TFlong type, Title title, int column)
{
}
public void Accept(TFloat type, Title title, int column)
{
}
public void Accept(TDouble type, Title title, int column)
{
}
public void Accept(TEnum type, Title title, int column)
{
}
public void Accept(TString type, Title title, int column)
{
}
public void Accept(TBytes type, Title title, int column)
{
}
public void Accept(TText type, Title title, int column)
{
// 默认使用 # 来分割
if (!title.Tags.ContainsKey("sep"))
{
title.Tags.Add("sep", "#");
}
}
public void Accept(TBean type, Title title, int column)
{
title.FromIndex = column;
title.ToIndex = column;
if (!title.Tags.ContainsKey("sep"))
{
title.Tags.Add("sep", "|");
}
//int lastColumn = column - 1;
//if (type.IsDynamic)
//{
// title.AddSubTitle(new Title()
// {
// Name = "__type__",
// FromIndex = lastColumn + 1,
// ToIndex = lastColumn + 1,
// Tags = new Dictionary<string, string>(),
// });
// ++lastColumn;
//}
//foreach (var f in type.Bean.HierarchyFields)
//{
// int startColumn = lastColumn + 1;
// var subTitle = new Title()
// {
// Name = f.Name,
// FromIndex = startColumn,
// ToIndex = startColumn,
// Tags = new Dictionary<string, string>(),
// };
// if (f.CType.Tags.TryGetValue("sep", out var sep))
// {
// subTitle.Tags.Add("sep", sep);
// }
// if (f.CType.Tags.TryGetValue("multi_rows", out var multiRows))
// {
// subTitle.Tags.Add("multiRows", multiRows);
// }
// f.CType.Apply(this, subTitle, startColumn);
// lastColumn = subTitle.ToIndex;
// title.AddSubTitle(subTitle);
//}
//title.ToIndex = Math.Max(lastColumn, column);
//int maxFieldNum = 20;
//if (type.IsDynamic)
//{
// if (!title.Tags.ContainsKey("sep"))
// {
// title.Tags.Add("sep", "|");
// }
// //var maxFieldCount = type.Bean.HierarchyNotAbstractChildren.Max(c => c.HierarchyFields.Count);
// //var fields = type.Bean.HierarchyNotAbstractChildren.SelectMany(c => c.HierarchyFields).Select(f => f.Name).ToHashSet();
// title.ToIndex = column;
//}
//else
//{
// int lastColumn = column - 1;
// foreach (var f in type.Bean.HierarchyFields)
// {
// int startColumn = lastColumn + 1;
// var subTitle = new Title()
// {
// Name = f.Name,
// FromIndex = startColumn,
// ToIndex = startColumn,
// Tags = new Dictionary<string, string>(),
// };
// if (f.CType.Tags.TryGetValue("sep", out var sep))
// {
// subTitle.Tags.Add("sep", sep);
// }
// if (f.CType.Tags.TryGetValue("multi_rows", out var multiRows))
// {
// subTitle.Tags.Add("multiRows", multiRows);
// }
// f.CType.Apply(this, subTitle, startColumn);
// lastColumn = subTitle.ToIndex;
// title.AddSubTitle(subTitle);
// }
// title.ToIndex = Math.Max(lastColumn, column);
//}
}
public void Accept(TArray type, Title title, int column)
{
if (!type.ElementType.Apply(IsNotSepTypeVisitor.Ins))
{
title.Tags.TryAdd("sep", "|");
}
}
public void Accept(TList type, Title title, int column)
{
if (!type.ElementType.Apply(IsNotSepTypeVisitor.Ins))
{
title.Tags.TryAdd("sep", "|");
}
}
public void Accept(TSet type, Title title, int column)
{
if (!type.ElementType.Apply(IsNotSepTypeVisitor.Ins))
{
title.Tags.TryAdd("sep", "|");
}
}
public void Accept(TMap type, Title title, int column)
{
title.Tags.TryAdd("sep", "|");
}
public void Accept(TVector2 type, Title title, int column)
{
}
public void Accept(TVector3 type, Title title, int column)
{
}
public void Accept(TVector4 type, Title title, int column)
{
}
public void Accept(TDateTime type, Title title, int column)
{
}
}
}

View File

@ -10,9 +10,9 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace LubanAssistant namespace Luban.Job.Cfg.DataConverts
{ {
class ToExcelStringVisitor : IDataFuncVisitor<string, string> public class ToExcelStringVisitor : IDataFuncVisitor<string, string>
{ {
public static ToExcelStringVisitor Ins { get; } = new(); public static ToExcelStringVisitor Ins { get; } = new();

View File

@ -252,7 +252,7 @@ namespace Luban.Job.Cfg.DataCreators
} }
else if (d is string s) else if (d is string s)
{ {
return DataUtil.UnEscapeString(s); return DataUtil.UnEscapeRawString(s);
} }
else else
{ {

View File

@ -115,7 +115,7 @@ namespace Luban.Job.Cfg.DataCreators
} }
else else
{ {
throw new Exception($"{x} 不是 double 类型数据"); throw new Exception($"{x} 不是 string 类型数据");
} }
} }

View File

@ -212,7 +212,7 @@ namespace Luban.Job.Cfg.DataCreators
} }
else if (d is string s) else if (d is string s)
{ {
return DataUtil.UnEscapeString(s); return DataUtil.UnEscapeRawString(s);
} }
else else
{ {

View File

@ -17,7 +17,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
private const int TITLE_MAX_ROW_NUM = 10; private const int TITLE_MAX_ROW_NUM = 10;
private const int TITLE_DEFAULT_ROW_NUM = 3; private const int TITLE_DEFAULT_ROW_NUM = 3;
private static System.Text.Encoding DetectCsvEncoding(Stream fs) public static System.Text.Encoding DetectCsvEncoding(Stream fs)
{ {
Ude.CharsetDetector cdet = new Ude.CharsetDetector(); Ude.CharsetDetector cdet = new Ude.CharsetDetector();
cdet.Feed(fs); cdet.Feed(fs);

View File

@ -1,6 +1,7 @@
using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.Defs; using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils; using Luban.Job.Cfg.Utils;
using System.Text.RegularExpressions;
namespace Luban.Job.Cfg.DataVisitors namespace Luban.Job.Cfg.DataVisitors
{ {

View File

@ -0,0 +1,159 @@
using Luban.Job.Cfg.Cache;
using Luban.Job.Cfg.DataConverts;
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Utils;
using SpreadsheetLight;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.Generate
{
[Render("convert_xlsx")]
[Render("convert_excel")]
class ExcelConvertRender : DataRenderBase
{
public override void Render(GenContext ctx)
{
string genType = ctx.GenType;
foreach (var table in ctx.ExportTables)
{
ctx.Tasks.Add(Task.Run(() =>
{
var records = ctx.Assembly.GetTableAllDataList(table);
string dirName = table.FullName;
var fileName = table.FullName;
var filePath = $"{dirName}/{fileName}.xlsx";
var title = TitleCreator.Ins.CreateTitle(table);
var dataRangeArray = new List<object[]>();
dataRangeArray.Add(new object[] { "##" });
var titleRow = new object[title.ToIndex + 1];
foreach (var subTitle in title.SubTitleList)
{
string titleAndTags = subTitle.Tags.Count == 0 ? subTitle.Name : subTitle.Name + "&" + string.Join('&', subTitle.Tags.Select(e => $"{e.Key}={e.Value}"));
titleRow[subTitle.FromIndex] = titleAndTags;
}
dataRangeArray.Add(titleRow);
// 注释行1
dataRangeArray.Add(Array.Empty<object>());
// 注释行2
dataRangeArray.Add(Array.Empty<object>());
int totalRowCount = dataRangeArray.Count;
foreach (var rec in records)
{
var fillVisitor = new FillSheetVisitor(dataRangeArray, title.ToIndex + 1, totalRowCount);
totalRowCount += rec.Data.Apply(fillVisitor, title);
}
//var memoryStream = new MemoryStream();
//using (var package = new ExcelPackage(memoryStream))
//{
// var sheet = package.Workbook.Worksheets.Add("sheet1");
// for (int i = 0; i < dataRangeArray.Count; i++)
// {
// var rawRow = dataRangeArray[i];
// sheet.Cells[i + 1, 1, i + 1, rawRow.Length].FillList(rawRow);
// }
// sheet.Cells.AutoFitColumns();
// content = package.GetAsByteArray();
//}
var worksheet = new SLDocument();
//var rows = new List<Row>();
//for (int i = 0; i < dataRangeArray.Count; i++)
//{
// var rawRow = dataRangeArray[i];
// var cells = new List<Cell>();
// for (int j = 0; j < rawRow.Length; j++)
// {
// cells.Add(new Cell(j + 1, rawRow[j]));
// }
// rows.Add(new Row(i + 1, cells));
//}
//worksheet.Rows = rows;
for (int i = 0; i < dataRangeArray.Count; i++)
{
var rawRow = dataRangeArray[i];
for (int j = 0; j < rawRow.Length; j++)
{
object v = dataRangeArray[i][j];
if (v != null)
{
switch (v)
{
case int t:
{
worksheet.SetCellValue(i + 1, j + 1, t);
break;
}
case string t:
{
worksheet.SetCellValue(i + 1, j + 1, t);
break;
}
case float t:
{
worksheet.SetCellValue(i + 1, j + 1, t);
break;
}
case bool t:
{
worksheet.SetCellValue(i + 1, j + 1, t);
break;
}
case DateTime t:
{
worksheet.SetCellValue(i + 1, j + 1, t);
break;
}
default:
{
worksheet.SetCellValue(i + 1, j + 1, v.ToString());
break;
}
}
}
}
}
worksheet.AutoFitColumn(1, title.ToIndex + 1);
worksheet.AutoFitRow(1, dataRangeArray.Count);
var stream = new MemoryStream();
worksheet.SaveAs(stream);
//var tempFile = $"{Path.GetTempFileName()}_{fileName}.tmp";
//var outputFile = $"{Path.GetTempFileName()}_{fileName}.xlsx";
//var outputStream = new FileStream(tempFile, FileMode.CreateNew, FileAccess.ReadWrite);
//var writer = ExcelDataWriter.ExcelDataWriter.GetAsByteArray(dataRangeArray, new ExcelDataWriter.ClassMap<object[]>());
//using (FastExcel.FastExcel fastExcel = new FastExcel.FastExcel(new System.IO.FileInfo(tempFile), new System.IO.FileInfo(outputFile)))
//{
// // Write the data
// fastExcel.Write(worksheet, "sheet1");
//}
//outputStream.Close();
//outputStream.Flush();
byte[] content = DataUtil.StreamToBytes(stream);
var md5 = CacheFileUtil.GenStringOrBytesMd5AndAddCache(filePath, content);
FileRecordCacheManager.Ins.AddCachedRecordOutputData(table, records, genType, md5);
ctx.GenDataFilesInOutputDataDir.Add(new Luban.Common.Protos.FileInfo() { FilePath = filePath, MD5 = md5 });
}));
}
}
}
}

View File

@ -8,7 +8,7 @@ namespace Luban.Job.Cfg.Generate
{ {
[Render("convert_json")] [Render("convert_json")]
[Render("convert_lua")] [Render("convert_lua")]
class ConvertRender : DataRenderBase class TextConvertRender : DataRenderBase
{ {
public override void Render(GenContext ctx) public override void Render(GenContext ctx)
{ {

View File

@ -14,7 +14,7 @@ namespace Luban.Job.Cfg.Utils
{ {
static class DataConvertUtil static class DataConvertUtil
{ {
public static object ToConvertRecord(DefTable table, Record record, string converType) public static string ToConvertRecord(DefTable table, Record record, string converType)
{ {
switch (converType) switch (converType)
{ {
@ -38,28 +38,6 @@ namespace Luban.Job.Cfg.Utils
{ {
return new LuaConvertor().ExportRecord(table, record); return new LuaConvertor().ExportRecord(table, record);
} }
//case "data_erlang":
//{
// var content = new StringBuilder();
// switch (table.Mode)
// {
// case ETableMode.ONE:
// {
// ErlangExport.Ins.ExportTableSingleton(table, records[0], content);
// break;
// }
// case ETableMode.MAP:
// {
// ErlangExport.Ins.ExportTableMap(table, records, content);
// break;
// }
// default:
// {
// throw new NotSupportedException();
// }
// }
// return content.ToString();
//}
default: default:
{ {
throw new ArgumentException($"not support datatype:{converType}"); throw new ArgumentException($"not support datatype:{converType}");

View File

@ -77,7 +77,7 @@ namespace Luban.Job.Cfg.Utils
return bytes; return bytes;
} }
public static string UnEscapeString(string s) public static string UnEscapeRawString(string s)
{ {
switch (s) switch (s)
{ {
@ -89,13 +89,13 @@ namespace Luban.Job.Cfg.Utils
public static string EscapeString(string s) public static string EscapeString(string s)
{ {
return s.Replace("\\", "\\\\"); return s.Replace("\\", "\\\\").Replace("\"", "\\\"");
} }
public static string EscapeStringWithQuote(string s) //public static string EscapeStringWithQuote(string s)
{ //{
return "\"" + s.Replace("\\", "\\\\") + "\""; // return "\"" + s.Replace("\\", "\\\\") + "\"";
} //}
public static (string Key, string Text) ExtractText(string rawKeyAndText) public static (string Key, string Text) ExtractText(string rawKeyAndText)
{ {

View File

@ -10,8 +10,6 @@ namespace Luban.Job.Cfg.Utils
{ {
static class ValidatorUtil static class ValidatorUtil
{ {
private static void CreateValidatorsForType(TType type) private static void CreateValidatorsForType(TType type)
{ {
foreach (var valName in ValidatorFactory.ValidatorNames) foreach (var valName in ValidatorFactory.ValidatorNames)

View File

@ -95,6 +95,7 @@ namespace Luban.Job.Common.Utils
{ "yml", "yml" }, { "yml", "yml" },
{ "erlang", "erl" }, { "erlang", "erl" },
{ "erl", "erl" }, { "erl", "erl" },
{ "xlsx", "xlsx" },
}; };
public static string GetOutputFileSuffix(string genType) public static string GetOutputFileSuffix(string genType)

View File

@ -1,4 +1,5 @@
using Luban.Common.Utils; using Luban.Common.Utils;
using Luban.Job.Cfg.DataConverts;
using Luban.Job.Cfg.DataExporters; using Luban.Job.Cfg.DataExporters;
using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources.Excel; using Luban.Job.Cfg.DataSources.Excel;

View File

@ -249,6 +249,12 @@
<Compile Include="..\Luban.Common\Source\Utils\XmlUtil.cs"> <Compile Include="..\Luban.Common\Source\Utils\XmlUtil.cs">
<Link>Source\Utils\XmlUtil.cs</Link> <Link>Source\Utils\XmlUtil.cs</Link>
</Compile> </Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataConverts\FillSheetVisitor.cs">
<Link>Source\DataConverters\FillSheetVisitor.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataConverts\ToExcelStringVisitor.cs">
<Link>Source\DataConverters\ToExcelStringVisitor.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataCreators\DataCreateException.cs"> <Compile Include="..\Luban.Job.Cfg\Source\DataCreators\DataCreateException.cs">
<Link>Source\DataCreators\DataCreateException.cs</Link> <Link>Source\DataCreators\DataCreateException.cs</Link>
</Compile> </Compile>
@ -634,12 +640,10 @@
<DependentUpon>AssistantTab.cs</DependentUpon> <DependentUpon>AssistantTab.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="ExcelUtil.cs" /> <Compile Include="ExcelUtil.cs" />
<Compile Include="FillSheetVisitor.cs" />
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Source\DataExporters\RawJsonExportor.cs" /> <Compile Include="Source\DataExporters\RawJsonExportor.cs" />
<Compile Include="ToExcelStringVisitor.cs" />
<EmbeddedResource Include="AssistantTab.resx"> <EmbeddedResource Include="AssistantTab.resx">
<DependentUpon>AssistantTab.cs</DependentUpon> <DependentUpon>AssistantTab.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
@ -653,7 +657,6 @@
<DependentUpon>Resources.resx</DependentUpon> <DependentUpon>Resources.resx</DependentUpon>
</Compile> </Compile>
<None Include="app.config" /> <None Include="app.config" />
<None Include="focus_creative_games.pfx" />
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>