【完善】LubanAssistant 能够正确从json或者其他配置文件中加载配置,并且填充到excel中

main
walon 2021-10-13 11:47:40 +08:00
parent 4e7c993d53
commit f22dc45273
12 changed files with 663 additions and 48 deletions

1
.gitignore vendored
View File

@ -270,3 +270,4 @@ __pycache__/
/config/output_lua
/config/output_lua_without_test
/src/Excel2TextDiff/Properties/launchSettings.json
/src/packages

View File

@ -109,7 +109,7 @@ namespace Luban.Job.Cfg.DataCreators
foreach (DefField f in bean.HierarchyFields)
{
string fname = f.Name;
Sheet.Title title = row.GetTitle(fname);
Title title = row.GetTitle(fname);
if (title == null)
{
throw new Exception($"bean:'{bean.FullName}' 缺失 列:'{fname}',请检查是否写错或者遗漏");
@ -288,7 +288,7 @@ namespace Luban.Job.Cfg.DataCreators
}
private bool TryCreateColumnStream(Sheet.NamedRow x, Sheet.Title title, out ExcelStream stream)
private bool TryCreateColumnStream(Sheet.NamedRow x, Title title, out ExcelStream stream)
{
var cells = new List<Cell>();
for (int i = title.FromIndex; i <= title.ToIndex; i++)

View File

@ -38,46 +38,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
public List<List<Cell>> RowColumns => _rowColumns;
public class Title
{
public bool Root { get; set; }
public int FromIndex { get; set; }
public int ToIndex { get; set; }
public string Name { get; set; }
public Dictionary<string, Title> SubTitles { get; set; } = new Dictionary<string, Title>();
public List<Title> SubTitleList { get; set; } = new List<Title>();
public void AddSubTitle(Title title)
{
if (!SubTitles.TryAdd(title.Name, title))
{
throw new Exception($"标题:{title.Name} 重复");
}
SubTitleList.Add(title);
}
// 由于先处理merge再处理只占一列的标题头.
// sub titles 未必是有序的。对于大多数数据并无影响
// 但对于 list类型的多级标题头有可能导致element 数据次序乱了
public void SortSubTitles()
{
SubTitleList.Sort((t1, t2) => t1.FromIndex - t2.FromIndex);
foreach (var t in SubTitleList)
{
t.SortSubTitles();
}
}
public override string ToString()
{
return $"name:{Name} [{FromIndex}, {ToIndex}] sub titles:[{string.Join(",\\n", SubTitleList)}]";
}
}
public class NamedRow
{
@ -336,6 +297,10 @@ namespace Luban.Job.Cfg.DataSources.Excel
HeaderRowCount = v;
break;
}
case "table":
{
break;
}
default:
{
throw new Exception($"非法单元薄 meta 属性定义 {attr}, 合法属性有: orientation=r|row|c|column,title_rows=<number>");

View File

@ -0,0 +1,49 @@
using Bright.Collections;
using System;
using System.Collections.Generic;
namespace Luban.Job.Cfg.DataSources.Excel
{
public class Title
{
public bool Root { get; set; }
public int FromIndex { get; set; }
public int ToIndex { get; set; }
public string Name { get; set; }
public string Sep { get; set; } = "|";
public Dictionary<string, Title> SubTitles { get; set; } = new Dictionary<string, Title>();
public List<Title> SubTitleList { get; set; } = new List<Title>();
public void AddSubTitle(Title title)
{
if (!SubTitles.TryAdd(title.Name, title))
{
throw new Exception($"标题:{title.Name} 重复");
}
SubTitleList.Add(title);
}
// 由于先处理merge再处理只占一列的标题头.
// sub titles 未必是有序的。对于大多数数据并无影响
// 但对于 list类型的多级标题头有可能导致element 数据次序乱了
public void SortSubTitles()
{
SubTitleList.Sort((t1, t2) => t1.FromIndex - t2.FromIndex);
foreach (var t in SubTitleList)
{
t.SortSubTitles();
}
}
public override string ToString()
{
return $"name:{Name} [{FromIndex}, {ToIndex}] sub titles:[{string.Join(",\\n", SubTitleList)}]";
}
}
}

View File

@ -62,6 +62,11 @@ namespace Luban.Job.Cfg.Utils
return new DDateTime(dateTime);
}
public static string FormatDateTime(DateTime datetime)
{
return datetime.ToString("yyyy-M-d HH:mm:ss");
}
public static byte[] StreamToBytes(Stream stream)
{
byte[] bytes = new byte[stream.Length];

View File

@ -2,6 +2,7 @@
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Defs;
using Luban.Server.Common;
using Microsoft.Office.Interop.Excel;
using Microsoft.Office.Tools.Ribbon;
using System;
using System.Collections.Concurrent;
@ -101,15 +102,27 @@ namespace LubanAssistant
}
}
private bool TryGetTableName(out string tableName)
{
tableName = "test.TbExcelFromJson";
return true;
Worksheet cur = Globals.LubanAssistant.Application.ActiveSheet;
var metaAttrs = ExcelUtil.ParseMetaAttrs(cur);
if (metaAttrs.TryGetValue("table", out tableName))
{
return true;
}
tableName = null;
return false;
}
private void LoadDataToCurrentDoc()
{
if (!TryGetTableName(out var tableName))
Worksheet sheet = Globals.LubanAssistant.Application.ActiveSheet;
var metaAttrs = ExcelUtil.ParseMetaAttrs(sheet);
if (!metaAttrs.TryGetValue("table", out var tableName))
{
MessageBox.Show($"meta行未指定table名");
return;
@ -119,7 +132,9 @@ namespace LubanAssistant
{
try
{
await LoadUtil.LoadTableDataToCurrentWorkSheetAsync(RootDefineFile, InputDataDir, tableName);
var tableDataInfo = await DataLoaderUtil.LoadTableDataAsync(RootDefineFile, InputDataDir, tableName);
var title = ExcelUtil.ParseTitles(sheet);
ExcelUtil.FillRecords(sheet, metaAttrs, title, tableDataInfo);
}
catch (Exception e)
{

View File

@ -0,0 +1,166 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources.Excel;
using Luban.Job.Cfg.Defs;
using Microsoft.Office.Interop.Excel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LubanAssistant
{
static class ExcelUtil
{
public static Dictionary<string, string> ParseMetaAttrs(Worksheet sheet)
{
Range metaRow = sheet.Rows[1];
if (metaRow.Cells[1, 1].Text.ToString() != "##")
{
throw new Exception("A1 should be ##");
}
var metaAttrs = new Dictionary<string, string>();
for (int i = 2, n = sheet.UsedRange.Columns.Count; i <= n; i++)
{
Range cell = metaRow.Cells[1, i];
string value = cell.Value?.ToString();
if (!string.IsNullOrWhiteSpace(value))
{
var attrs = value.Split('=');
if (attrs.Length != 2)
{
throw new Exception($"invalid meta attr:{value}");
}
metaAttrs.Add(attrs[0], attrs[1]);
}
}
return metaAttrs;
}
public static TitleInfo ParseTitles(Worksheet sheet)
{
int titleRows = 1;
Range c1 = sheet.Cells[2, 1];
if (c1.MergeCells)
{
titleRows = c1.MergeArea.Count;
}
var rootTile = new Title()
{
FromIndex = 2,
ToIndex = sheet.UsedRange.Columns.Count,
Name = "__root__",
Root = true,
};
ParseSubTitle(sheet, 2, titleRows + 1, rootTile);
return new TitleInfo(rootTile, titleRows);
}
private static void ParseSubTitle(Worksheet sheet, int rowIndex, int maxRowIndex, Title title)
{
Range row = sheet.Rows[rowIndex];
for (int i = title.FromIndex; i <= title.ToIndex; i++)
{
Range subTitleRange = row.Cells[1, i];
string subTitleValue = subTitleRange.Value?.ToString();
if (string.IsNullOrWhiteSpace(subTitleValue))
{
continue;
}
var attrs = subTitleValue.Split('&');
string subTitleName = attrs[0];
string sep = "";
foreach (var attrPair in attrs.Skip(1))
{
var pairs = attrPair.Split('=');
if (pairs.Length != 2)
{
throw new Exception($"invalid title: {subTitleValue}");
}
switch (pairs[0])
{
case "sep":
{
sep = pairs[1];
break;
}
default:
{
throw new Exception($"invalid title: {subTitleValue}");
}
}
}
if (title.SubTitles.ContainsKey(subTitleName))
{
throw new Exception($"title:{subTitleName} 重复");
}
var newSubTitle = new Title()
{
Name = subTitleName,
FromIndex = i,
ToIndex = i,
};
if (!string.IsNullOrWhiteSpace(sep))
{
newSubTitle.Sep = sep;
}
if (subTitleRange.MergeCells)
{
newSubTitle.ToIndex = i + subTitleRange.MergeArea.Count - 1;
}
else
{
newSubTitle.ToIndex = i;
}
title.SubTitles.Add(subTitleName, newSubTitle);
}
title.SubTitleList.AddRange(title.SubTitles.Values);
if (rowIndex < maxRowIndex)
{
foreach (var subTitle in title.SubTitleList)
{
ParseSubTitle(sheet, rowIndex + 1, maxRowIndex, subTitle);
}
}
}
public static void FillRecords(Worksheet sheet, Dictionary<string, string> metaAttrs, TitleInfo title, TableDataInfo tableDataInfo)
{
int titleRowNum = 3;
if (metaAttrs.TryGetValue("title_rows", out var titleRowsStr) && !int.TryParse(titleRowsStr, out titleRowNum))
{
throw new Exception($"meta 属性 title_rows 不合法");
}
if (titleRowNum < title.RowNum)
{
throw new Exception($"meta 属性title_rows不能比字段名行的行数小");
}
int usedRowNum = sheet.UsedRange.Rows.Count;
if (usedRowNum > titleRowNum + 1)
{
Range allDataRange = sheet.Range[$"A{titleRowNum + 2},A{usedRowNum}"].EntireRow;
allDataRange.ClearContents();
}
int nextRowIndex = titleRowNum + 2;
foreach (var rec in tableDataInfo.MainRecords)
{
var fillVisitor = new FillSheetVisitor(sheet, nextRowIndex);
//FillRecord(sheet, ref nextRowIndex, title.RootTitle, rec);
nextRowIndex += rec.Data.Apply(fillVisitor, title.RootTitle);
}
}
//public static void FillRecord(Worksheet sheet, ref int nextRowIndex, Title title, Record record)
//{
// nextRowIndex += FillField(sheet, nextRowIndex, title, record.Data);
//}
}
}

View File

@ -0,0 +1,232 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataSources.Excel;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Microsoft.Office.Interop.Excel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LubanAssistant
{
class FillSheetVisitor : IDataFuncVisitor<Title, int>
{
private readonly Worksheet _sheet;
private readonly Range _cells;
private readonly int _startRowIndex;
public FillSheetVisitor(Worksheet sheet, int startRowIndex)
{
_sheet = sheet;
_cells = sheet.Cells;
_startRowIndex = startRowIndex;
}
public int Accept(DBool type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DByte type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DShort type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DFshort type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DInt type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DFint type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DLong type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DFlong type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DFloat type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DDouble type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DEnum type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.StrValue;
return 1;
}
public int Accept(DString type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Value;
return 1;
}
public int Accept(DBytes type, Title x)
{
throw new NotImplementedException();
}
public int Accept(DText type, Title x)
{
if (x.FromIndex == x.ToIndex)
{
throw new Exception($"title:{x.Name}为text类型至少要占两列");
}
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Key;
(_cells[_startRowIndex, x.FromIndex + 1] as Range).Value = type.RawValue;
return 1;
}
public int Accept(DBean type, Title x)
{
if (x.SubTitleList.Count > 0)
{
if (type.Type.IsAbstractType)
{
if (!x.SubTitles.TryGetValue(DefBean.TYPE_NAME_KEY, out var typeTitle))
{
throw new Exception($"多态bean:{type.Type.FullName} 缺失 __type__ 标题列");
}
if (type.ImplType != null)
{
(_cells[_startRowIndex, typeTitle.FromIndex] as Range).Value = type.ImplType.Name;
}
else
{
(_cells[_startRowIndex, typeTitle.FromIndex] as Range).Value = DefBean.BEAN_NULL_STR;
}
}
else
{
if (type.ImplType != null)
{
}
else
{
//(_cells[_startRowIndex, x.FromIndex] as Range).Value = "null";
throw new Exception($"title:{x.Name} 不支持 值为null的普通bean");
}
}
if (type.ImplType != null)
{
int index = 0;
foreach (var field in type.ImplType.HierarchyFields)
{
var data = type.Fields[index++];
if (!x.SubTitles.TryGetValue(field.Name, out var fieldTitle))
{
throw new Exception($"title:{x.Name} 子title:{field.Name} 缺失");
}
if (data != null)
{
//if (fieldTitle.SubTitleList.Count > 0)
//{
data.Apply(this, fieldTitle);
//}
//else
//{
// (_cells[_startRowIndex, fieldTitle.FromIndex] as Range).Value = data.Apply(ToExcelStringVisitor.Ins, fieldTitle.Sep);
//}
}
}
}
}
else
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
}
return 1;
}
public int Accept(DArray type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
return 1;
}
public int Accept(DList type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
return 1;
}
public int Accept(DSet type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
return 1;
}
public int Accept(DMap type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Apply(ToExcelStringVisitor.Ins, x.Sep);
return 1;
}
public int Accept(DVector2 type, Title x)
{
var v = type.Value;
(_cells[_startRowIndex, x.FromIndex] as Range).Value = $"{v.X},{v.Y}";
return 1;
}
public int Accept(DVector3 type, Title x)
{
var v = type.Value;
(_cells[_startRowIndex, x.FromIndex] as Range).Value = $"{v.X},{v.Y},{v.Z}";
return 1;
}
public int Accept(DVector4 type, Title x)
{
var v = type.Value;
(_cells[_startRowIndex, x.FromIndex] as Range).Value = $"{v.X},{v.Y},{v.Z},{v.W}";
return 1;
}
public int Accept(DDateTime type, Title x)
{
(_cells[_startRowIndex, x.FromIndex] as Range).Value = type.Time;
return 1;
}
}
}

View File

@ -1,8 +1,10 @@
using Bright.Common;
using Bright.Time;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Defs;
using Luban.Server.Common;
using Microsoft.Office.Interop.Excel;
using System;
using System.Collections.Generic;
using System.Linq;
@ -19,9 +21,6 @@ namespace LubanAssistant
var tableDataInfo = await DataLoaderUtil.LoadTableDataAsync(rootDefineFile, inputDataDir, tableName);
Console.WriteLine("load record num:{0}", tableDataInfo.MainRecords.Count);
Microsoft.Office.Interop.Excel.Worksheet cur = Globals.LubanAssistant.Application.ActiveSheet;
Console.WriteLine("== active sheet name:{0}", cur.Name);
}
}
}

View File

@ -270,6 +270,9 @@
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\Sheet.cs">
<Link>Source\DataSources\Excel\Sheet.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Excel\Title.cs">
<Link>Source\DataSources\Excel\Title.cs</Link>
</Compile>
<Compile Include="..\Luban.Job.Cfg\Source\DataSources\Json\JsonDataSource.cs">
<Link>Source\DataSources\Json\JsonDataSource.cs</Link>
</Compile>
@ -531,10 +534,13 @@
<Compile Include="AssistantTab.Designer.cs">
<DependentUpon>AssistantTab.cs</DependentUpon>
</Compile>
<Compile Include="ExcelUtil.cs" />
<Compile Include="FillSheetVisitor.cs" />
<Compile Include="LoadUtil.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ToExcelStringVisitor.cs" />
<EmbeddedResource Include="AssistantTab.resx">
<DependentUpon>AssistantTab.cs</DependentUpon>
</EmbeddedResource>
@ -559,6 +565,7 @@
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="Source\DataSources\Excel\TitleInfo.cs" />
<Compile Include="Source\Utils\AtomicLong.cs" />
<Compile Include="Source\Utils\CacheFileUtil.cs" />
<Compile Include="Source\Collections\CollectionExtension.cs" />

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Luban.Job.Cfg.DataSources.Excel
{
class TitleInfo
{
public Title RootTitle { get; }
public int RowNum { get; }
public TitleInfo(Title rootTitle, int rowNum)
{
RootTitle = rootTitle;
RowNum = rowNum;
}
}
}

View File

@ -0,0 +1,155 @@
using Luban.Job.Cfg.Datas;
using Luban.Job.Cfg.DataVisitors;
using Luban.Job.Cfg.Defs;
using Luban.Job.Cfg.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LubanAssistant
{
class ToExcelStringVisitor : IDataFuncVisitor<string, string>
{
public static ToExcelStringVisitor Ins { get; } = new();
public string Accept(DBool type, string sep)
{
return type.Value ? "true" : "false";
}
public string Accept(DByte type, string sep)
{
return type.Value.ToString();
}
public string Accept(DShort type, string sep)
{
return type.Value.ToString();
}
public string Accept(DFshort type, string sep)
{
return type.Value.ToString();
}
public string Accept(DInt type, string sep)
{
return type.Value.ToString();
}
public string Accept(DFint type, string sep)
{
return type.Value.ToString();
}
public string Accept(DLong type, string sep)
{
return type.Value.ToString();
}
public string Accept(DFlong type, string sep)
{
return type.Value.ToString();
}
public string Accept(DFloat type, string sep)
{
return type.Value.ToString();
}
public string Accept(DDouble type, string sep)
{
return type.Value.ToString();
}
public string Accept(DEnum type, string sep)
{
return type.StrValue;
}
private string Enscape(string s)
{
return string.IsNullOrEmpty(s) ? "\"\"" : s;
}
public string Accept(DString type, string sep)
{
return Enscape(type.Value.ToString());
}
public string Accept(DBytes type, string sep)
{
throw new NotImplementedException();
}
public string Accept(DText type, string sep)
{
return $"{Enscape(type.Key)}{sep}{Enscape(type.RawValue)}";
}
public string Accept(DBean type, string sep)
{
var sb = new List<string>();
if (type.Type.IsAbstractType)
{
sb.Add(type.ImplType != null ? type.ImplType.Name : DefBean.BEAN_NULL_STR);
}
foreach (var field in type.Fields)
{
if (field == null)
{
sb.Add("null");
continue;
}
sb.Add(field.Apply(this, sep));
}
return string.Join(sep, sb);
}
public string Accept(DArray type, string sep)
{
return string.Join(sep, type.Datas.Select(d => d.Apply(this, sep)));
}
public string Accept(DList type, string sep)
{
return string.Join(sep, type.Datas.Select(d => d.Apply(this, sep)));
}
public string Accept(DSet type, string sep)
{
return string.Join(sep, type.Datas.Select(d => d.Apply(this, sep)));
}
public string Accept(DMap type, string sep)
{
return string.Join(sep, type.Datas.Select(d => $"{d.Key.Apply(this, sep)}{sep}{d.Value.Apply(this, sep)}"));
}
public string Accept(DVector2 type, string sep)
{
var v = type.Value;
return $"{v.X},{v.Y}";
}
public string Accept(DVector3 type, string sep)
{
var v = type.Value;
return $"{v.X},{v.Y},{v.Z}";
}
public string Accept(DVector4 type, string sep)
{
var v = type.Value;
return $"{v.X},{v.Y},{v.Z},{v.W}";
}
public string Accept(DDateTime type, string sep)
{
return DataUtil.FormatDateTime(type.Time);
}
}
}