【重构】重构excel格式,更加优雅清晰
parent
881bd3aecc
commit
13309b5da8
17
README.md
17
README.md
|
|
@ -24,7 +24,8 @@ luban相较于常规的excel导表工具有以下核心优势:
|
||||||
- 增强了excel格式。可以比较简洁地excel配置**任意复杂**的数据,像子结构、结构列表,以及更复杂的深层次的嵌套结构都能直接解析处理。
|
- 增强了excel格式。可以比较简洁地excel配置**任意复杂**的数据,像子结构、结构列表,以及更复杂的深层次的嵌套结构都能直接解析处理。
|
||||||
- 完备的类型系统和多原始数据支持(xml、json、lua、yaml),可以轻松表达和解析**任意复杂**的数据。意味着传统excel导表工具无法处理的技能、行为树、副本等等复杂配置,luban也能够统一处理了,彻底将程序从复杂的配置解析中解放出来。
|
- 完备的类型系统和多原始数据支持(xml、json、lua、yaml),可以轻松表达和解析**任意复杂**的数据。意味着传统excel导表工具无法处理的技能、行为树、副本等等复杂配置,luban也能够统一处理了,彻底将程序从复杂的配置解析中解放出来。
|
||||||
- 完善的工作流支持。如id的外键引用检查;资源合法性检查;灵活的数据源定义(拆表或者多表合一);灵活的分组导出机制;多种本地化支持;生成极快(日常迭代300ms以内);Excel2TextDiff工具方便diff查看excel文件的版本间差异;
|
- 完善的工作流支持。如id的外键引用检查;资源合法性检查;灵活的数据源定义(拆表或者多表合一);灵活的分组导出机制;多种本地化支持;生成极快(日常迭代300ms以内);Excel2TextDiff工具方便diff查看excel文件的版本间差异;
|
||||||
- **=== LubanAssistant Excel插件 ===**。支持把json、lua、xml等文本格式的配置数据加载到excel中,批量编辑处理,最后再保存回原文件,较好地解决大型项目中多人合作数据编辑冲突合并的问题,较好解决在编辑器中制作的配置难以在excel中批量修改的问题。
|
- **LubanAssistant Excel插件**。支持把json、lua、xml等文本格式的配置数据加载到excel中,批量编辑处理,最后再保存回原文件,较好地解决大型项目中多人合作数据编辑冲突合并的问题,较好解决在编辑器中制作的配置难以在excel中批量修改的问题。
|
||||||
|
- 支持自定义代码与数据模板。强大的数据表达能力使得绝大多数项目的配置格式往往是luban的子集,因而有较低的项目迁移成本,利用模板重新适配代码和数据生成后,即使是研发已久或者上线项目也能从luban强大的数据处理能力中受益。
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
|
|
||||||
|
|
@ -45,12 +46,11 @@ luban相较于常规的excel导表工具有以下核心优势:
|
||||||
## 特性
|
## 特性
|
||||||
- 支持excel族、json、xml、lua、yaml 多种数据格式,基本统一了游戏常见的配置数据
|
- 支持excel族、json、xml、lua、yaml 多种数据格式,基本统一了游戏常见的配置数据
|
||||||
- **强大完备的类型系统**。**可以优雅表达任意复杂的数据结构**。支持所有常见原生类型、datetime类型、容器类型list,set,map、枚举和结构、**多态结构**以及**可空类型**。
|
- **强大完备的类型系统**。**可以优雅表达任意复杂的数据结构**。支持所有常见原生类型、datetime类型、容器类型list,set,map、枚举和结构、**多态结构**以及**可空类型**。
|
||||||
- 支持增强的excel格式。可以在excel里比较简洁填写出非常复杂的数据(比如顶层字段包含"list,A"类型字段, 而A是结构并且其中又包含"list,B"类型字段,B也是结构并且包含"list,C"这样的字段...)。
|
- 支持增强的excel格式。可以在excel里比较简洁填写出任意复杂的嵌套数据。
|
||||||
- 生成代码清晰易读、良好模块化。特地支持运行时原子性热更新配置。
|
- 生成代码清晰易读、良好模块化。支持指定变量命名风格约定。特地支持运行时原子性热更新配置。
|
||||||
- 生成极快。支持常规的本地缓存增量生成模式,也支持云生成模式。MMORPG这样大型项目也能秒内生成。日常增量生成基本在300ms以内,项目后期极大节省了迭代时间。另外支持**watch监测模式**,数据目录变化立即重新生成。
|
|
||||||
- 灵活的数据源定义。一个表可以来自多个文件或者一个文件内定义多个表或者一个目录下所有文件甚至来自云表格,以及以上的组合
|
- 灵活的数据源定义。一个表可以来自多个文件或者一个文件内定义多个表或者一个目录下所有文件甚至来自云表格,以及以上的组合
|
||||||
- 支持表与字段级别分组。可以灵活定义分组,选择性地导出客户端或者服务器或编辑器所用的表及字段
|
- 支持表与字段级别分组。可以灵活定义分组,选择性地导出客户端或者服务器或编辑器所用的表及字段
|
||||||
- 多种导出数据格式支持。支持binary、json、lua、xml 等导出数据格式
|
- 多种导出数据格式支持。支持binary、json、lua、xml、erlang、**xlsx** 及自定义的导出数据格式
|
||||||
- 强大灵活的定制能力
|
- 强大灵活的定制能力
|
||||||
- 支持代码模板,可以用自定义模板定制生成的代码格式
|
- 支持代码模板,可以用自定义模板定制生成的代码格式
|
||||||
- **支持数据模板**,可以用模板文件定制导出格式。意味着可以在不改动现有程序代码的情况下,把luban当作**配置处理前端**,生成自定义格式的数据与自己项目的配置加载代码配合工作。开发已久的项目或者已经上线的老项目,也能从luban强大的数据处理工作流中获益
|
- **支持数据模板**,可以用模板文件定制导出格式。意味着可以在不改动现有程序代码的情况下,把luban当作**配置处理前端**,生成自定义格式的数据与自己项目的配置加载代码配合工作。开发已久的项目或者已经上线的老项目,也能从luban强大的数据处理工作流中获益
|
||||||
|
|
@ -67,6 +67,9 @@ luban相较于常规的excel导表工具有以下核心优势:
|
||||||
- 支持文本动态本地化。运行时动态切换所有text类型数据为目标本地化字符串。
|
- 支持文本动态本地化。运行时动态切换所有text类型数据为目标本地化字符串。
|
||||||
- 支持 main + patches 数据合并。在基础数据上,施加差分数据,生成最终完整数据,适用于制作有细微不同的多地区的配置数据。
|
- 支持 main + patches 数据合并。在基础数据上,施加差分数据,生成最终完整数据,适用于制作有细微不同的多地区的配置数据。
|
||||||
- [TODO] 【独创】 支持任意粒度和任意类型数据(如int,bean,list,map)的本地化。
|
- [TODO] 【独创】 支持任意粒度和任意类型数据(如int,bean,list,map)的本地化。
|
||||||
|
- 生成极快。支持常规的本地缓存增量生成模式,也支持云生成模式。MMORPG这样大型项目也能秒内生成。日常增量生成基本在300ms以内,项目后期极大节省了迭代时间。另外支持**watch监测模式**,数据目录变化立即重新生成。
|
||||||
|
- **LubanAssistant**,Luban的Excel插件。支持把json、lua、xml等文本格式的配置数据加载到excel中,批量编辑处理,最后再保存回原文件,较好地解决大型项目中多人合作数据编辑冲突合并的问题,较好解决在编辑器中制作的配置难以在excel中批量修改的问题。
|
||||||
|
- Excel2TextDiff。将excel转成文本后再diff,清晰对比excel版本之间内容变化。
|
||||||
- 支持主流的游戏开发语言
|
- 支持主流的游戏开发语言
|
||||||
- c++ (11+)
|
- c++ (11+)
|
||||||
- c# (.net framework 4+. dotnet core 3+)
|
- c# (.net framework 4+. dotnet core 3+)
|
||||||
|
|
@ -95,10 +98,6 @@ luban相较于常规的excel导表工具有以下核心优势:
|
||||||
- 其他家基于js的小程序平台
|
- 其他家基于js的小程序平台
|
||||||
- 其他所有支持lua的引擎和平台
|
- 其他所有支持lua的引擎和平台
|
||||||
- 其他所有支持js的引擎和平台
|
- 其他所有支持js的引擎和平台
|
||||||
- 扩展工具
|
|
||||||
- Excel2TextDiff。将excel转成文本后再diff,清晰对比excel版本之间内容变化。
|
|
||||||
- **LubanAssistant**,Luban的Excel插件。支持把json、lua、xml等文本格式的配置数据加载到excel中,批量编辑处理,最后再保存回原文件,较好地解决大型项目中多人合作数据编辑冲突合并的问题,较好解决在编辑器中制作的配置难以在excel中批量修改的问题。
|
|
||||||
|
|
||||||
|
|
||||||
## 增强的excel格式
|
## 增强的excel格式
|
||||||
luban支持在excel中解析任意复杂的数据结构,哪怕复杂如技能、行为树(但在实践中一般使用编辑器制作这些数据,以json格式保存,而不会在excel里填写)。下面从简单到复杂展示在luban中配置这些数据的方式。
|
luban支持在excel中解析任意复杂的数据结构,哪怕复杂如技能、行为树(但在实践中一般使用编辑器制作这些数据,以json格式保存,而不会在excel里填写)。下面从简单到复杂展示在luban中配置这些数据的方式。
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using Luban.Job.Cfg.Utils;
|
using Luban.Job.Cfg.Utils;
|
||||||
using System;
|
using System;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Bright.Serialization;
|
using Bright.Serialization;
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.DataVisitors;
|
using Luban.Job.Cfg.DataVisitors;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.DataVisitors;
|
using Luban.Job.Cfg.DataVisitors;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.DataVisitors;
|
using Luban.Job.Cfg.DataVisitors;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using System;
|
using System;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.DataVisitors;
|
using Luban.Job.Cfg.DataVisitors;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using System;
|
using System;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.DataVisitors;
|
using Luban.Job.Cfg.DataVisitors;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,10 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (TitleRow row in sheet.GetRows())
|
foreach (var r in sheet.GetRows())
|
||||||
{
|
{
|
||||||
var tagRow = row.GetSubTitleNamedRow(TAG_KEY);
|
TitleRow row = r.Row;
|
||||||
string tagStr = tagRow?.Current?.ToString();
|
string tagStr = r.Tag;
|
||||||
if (DataUtil.IsIgnoreTag(tagStr))
|
if (DataUtil.IsIgnoreTag(tagStr))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
|
||||||
public string TableName { get; set; }
|
public string TableName { get; set; }
|
||||||
|
|
||||||
public int TitleRowCount { get; set; }
|
|
||||||
|
|
||||||
public List<List<Cell>> Cells { get; set; }
|
public List<List<Cell>> Cells { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
|
|
||||||
public string BriefDesc { get; set; }
|
public string Desc { get; set; }
|
||||||
|
|
||||||
public string DetailDesc { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RawSheetTableDefInfo
|
class RawSheetTableDefInfo
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
|
|
||||||
public string RawUrl { get; }
|
public string RawUrl { get; }
|
||||||
|
|
||||||
public List<TitleRow> Rows { get; } = new();
|
public List<(string Tag, TitleRow Row)> Rows { get; } = new();
|
||||||
|
|
||||||
public Sheet(string rawUrl, string name)
|
public Sheet(string rawUrl, string name)
|
||||||
{
|
{
|
||||||
|
|
@ -28,6 +28,11 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
this.Name = name;
|
this.Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetRowTag(List<Cell> row)
|
||||||
|
{
|
||||||
|
return row.Count > 0 ? row[0].Value?.ToString() ?? "" : "";
|
||||||
|
}
|
||||||
|
|
||||||
public void Load(RawSheet rawSheet)
|
public void Load(RawSheet rawSheet)
|
||||||
{
|
{
|
||||||
var cells = rawSheet.Cells;
|
var cells = rawSheet.Cells;
|
||||||
|
|
@ -41,14 +46,14 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Rows.Add(ParseOneLineTitleRow(title, row));
|
Rows.Add((GetRowTag(row), ParseOneLineTitleRow(title, row)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var oneRecordRows in SplitRows(title, cells))
|
foreach (var oneRecordRows in SplitRows(title, cells))
|
||||||
{
|
{
|
||||||
Rows.Add(ParseMultiLineTitleRow(title, oneRecordRows));
|
Rows.Add((GetRowTag(oneRecordRows[0]), ParseMultiLineTitleRow(title, oneRecordRows)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +183,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<TitleRow> GetRows()
|
public IEnumerable<(string Tag, TitleRow Row)> GetRows()
|
||||||
{
|
{
|
||||||
return Rows;
|
return Rows;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,6 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
{
|
{
|
||||||
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
private static readonly NLog.Logger s_logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
private const int TITLE_MIN_ROW_NUM = 2;
|
|
||||||
private const int TITLE_MAX_ROW_NUM = 10;
|
|
||||||
private const int TITLE_DEFAULT_ROW_NUM = 3;
|
|
||||||
|
|
||||||
public 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();
|
||||||
|
|
@ -65,48 +61,18 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
private static RawSheet ParseRawSheet(IExcelDataReader reader)
|
private static RawSheet ParseRawSheet(IExcelDataReader reader)
|
||||||
{
|
{
|
||||||
bool orientRow;
|
bool orientRow;
|
||||||
int titleRowNum;
|
|
||||||
|
|
||||||
if (!TryParseMeta(reader, out orientRow, out titleRowNum, out var tableName))
|
if (!TryParseMeta(reader, out orientRow, out var tableName))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var cells = ParseRawSheetContent(reader, orientRow);
|
var cells = ParseRawSheetContent(reader, orientRow, false);
|
||||||
var title = ParseTitle(cells, reader.MergeCells, orientRow, out _);
|
var title = ParseTitle(cells, reader.MergeCells, orientRow);
|
||||||
cells.RemoveRange(0, Math.Min(titleRowNum, cells.Count));
|
cells.RemoveAll(c => c.Count == 0 || IsHeaderRow(c));
|
||||||
return new RawSheet() { Title = title, TitleRowCount = titleRowNum, TableName = tableName, Cells = cells };
|
return new RawSheet() { Title = title, TableName = tableName, Cells = cells };
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetTitleRowNum(CellRange[] mergeCells, bool orientRow)
|
public static Title ParseTitle(List<List<Cell>> cells, CellRange[] mergeCells, bool orientRow)
|
||||||
{
|
|
||||||
if (mergeCells == null)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (orientRow)
|
|
||||||
{
|
|
||||||
foreach (var mergeCell in mergeCells)
|
|
||||||
{
|
|
||||||
if (mergeCell.FromRow == 1 && mergeCell.FromColumn == 0)
|
|
||||||
{
|
|
||||||
return mergeCell.ToRow - mergeCell.FromRow + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var mergeCell in mergeCells)
|
|
||||||
{
|
|
||||||
if (mergeCell.FromColumn == 1 && mergeCell.FromRow == 0)
|
|
||||||
{
|
|
||||||
return mergeCell.ToColumn - mergeCell.FromColumn + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Title ParseTitle(List<List<Cell>> cells, CellRange[] mergeCells, bool orientRow, out int titleRowNum)
|
|
||||||
{
|
{
|
||||||
var rootTitle = new Title()
|
var rootTitle = new Title()
|
||||||
{
|
{
|
||||||
|
|
@ -117,9 +83,9 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
ToIndex = cells.Select(r => r.Count).Max() - 1
|
ToIndex = cells.Select(r => r.Count).Max() - 1
|
||||||
};
|
};
|
||||||
|
|
||||||
titleRowNum = GetTitleRowNum(mergeCells, orientRow);
|
//titleRowNum = GetTitleRowNum(mergeCells, orientRow);
|
||||||
|
|
||||||
ParseSubTitles(rootTitle, cells, mergeCells, orientRow, 1, titleRowNum);
|
ParseSubTitles(rootTitle, cells, mergeCells, orientRow, 1);
|
||||||
|
|
||||||
rootTitle.Init();
|
rootTitle.Init();
|
||||||
|
|
||||||
|
|
@ -168,10 +134,10 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
return (titleName, tags);
|
return (titleName, tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ParseSubTitles(Title title, List<List<Cell>> cells, CellRange[] mergeCells, bool orientRow, int curDepth, int maxDepth)
|
private static void ParseSubTitles(Title title, List<List<Cell>> cells, CellRange[] mergeCells, bool orientRow, int curDepth)
|
||||||
{
|
{
|
||||||
|
var rowIndex = curDepth - 1;
|
||||||
var titleRow = cells[curDepth - 1];
|
var titleRow = cells[rowIndex];
|
||||||
if (mergeCells != null)
|
if (mergeCells != null)
|
||||||
{
|
{
|
||||||
foreach (var mergeCell in mergeCells)
|
foreach (var mergeCell in mergeCells)
|
||||||
|
|
@ -180,7 +146,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
if (orientRow)
|
if (orientRow)
|
||||||
{
|
{
|
||||||
//if (mergeCell.FromRow <= 1 && mergeCell.ToRow >= 1)
|
//if (mergeCell.FromRow <= 1 && mergeCell.ToRow >= 1)
|
||||||
if (mergeCell.FromRow == curDepth && mergeCell.FromColumn >= title.FromIndex && mergeCell.FromColumn <= title.ToIndex)
|
if (mergeCell.FromRow == rowIndex && mergeCell.FromColumn >= title.FromIndex && mergeCell.FromColumn <= title.ToIndex)
|
||||||
{
|
{
|
||||||
var nameAndAttrs = titleRow[mergeCell.FromColumn].Value?.ToString()?.Trim();
|
var nameAndAttrs = titleRow[mergeCell.FromColumn].Value?.ToString()?.Trim();
|
||||||
if (IsIgnoreTitle(nameAndAttrs))
|
if (IsIgnoreTitle(nameAndAttrs))
|
||||||
|
|
@ -194,7 +160,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (mergeCell.FromColumn == curDepth - 1 && mergeCell.FromRow - 1 >= title.FromIndex && mergeCell.FromRow - 1 <= title.ToIndex)
|
if (mergeCell.FromColumn == rowIndex && mergeCell.FromRow - 1 >= title.FromIndex && mergeCell.FromRow - 1 <= title.ToIndex)
|
||||||
{
|
{
|
||||||
// 标题 行
|
// 标题 行
|
||||||
var nameAndAttrs = titleRow[mergeCell.FromRow - 1].Value?.ToString()?.Trim();
|
var nameAndAttrs = titleRow[mergeCell.FromRow - 1].Value?.ToString()?.Trim();
|
||||||
|
|
@ -211,9 +177,9 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curDepth < maxDepth)
|
if (curDepth < cells.Count && IsSubFieldRow(cells[curDepth]))
|
||||||
{
|
{
|
||||||
ParseSubTitles(subTitle, cells, mergeCells, orientRow, curDepth + 1, maxDepth);
|
ParseSubTitles(subTitle, cells, mergeCells, orientRow, curDepth + 1);
|
||||||
}
|
}
|
||||||
title.AddSubTitle(subTitle);
|
title.AddSubTitle(subTitle);
|
||||||
|
|
||||||
|
|
@ -229,9 +195,9 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
}
|
}
|
||||||
var (titleName, tags) = ParseNameAndMetaAttrs(nameAndAttrs);
|
var (titleName, tags) = ParseNameAndMetaAttrs(nameAndAttrs);
|
||||||
|
|
||||||
if (title.SubTitles.TryGetValue(titleName, out var oldTitle))
|
if (title.SubTitles.TryGetValue(titleName, out var subTitle))
|
||||||
{
|
{
|
||||||
if (oldTitle.FromIndex != i)
|
if (subTitle.FromIndex != i)
|
||||||
{
|
{
|
||||||
throw new Exception($"列:{titleName} 重复");
|
throw new Exception($"列:{titleName} 重复");
|
||||||
}
|
}
|
||||||
|
|
@ -240,54 +206,46 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
title.AddSubTitle(new Title() { Name = titleName, Tags = tags, FromIndex = i, ToIndex = i });
|
subTitle = new Title() { Name = titleName, Tags = tags, FromIndex = i, ToIndex = i };
|
||||||
|
if (curDepth < cells.Count && IsSubFieldRow(cells[curDepth]))
|
||||||
|
{
|
||||||
|
ParseSubTitles(subTitle, cells, mergeCells, orientRow, curDepth + 1);
|
||||||
|
}
|
||||||
|
title.AddSubTitle(subTitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryParseMeta(List<string> cells, out bool orientRow, out int titleRows, out string tableName)
|
public static bool TryParseMeta(string metaStr, out bool orientRow, out string tableName)
|
||||||
{
|
{
|
||||||
orientRow = true;
|
orientRow = true;
|
||||||
titleRows = TITLE_DEFAULT_ROW_NUM;
|
|
||||||
tableName = "";
|
tableName = "";
|
||||||
|
|
||||||
// meta 行 必须以 ##为第一个单元格内容,紧接着 key:value 形式 表达meta属性
|
// meta 行 必须以 ##为第一个单元格内容,紧接着 key:value 形式 表达meta属性
|
||||||
if (cells.Count == 0 || cells[0] != "##")
|
if (!metaStr.StartsWith("##"))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var attr in cells.Skip(1))
|
foreach (var attr in metaStr.Substring(2).Split("&"))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(attr))
|
if (string.IsNullOrWhiteSpace(attr))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ss = attr.Split('=');
|
var sepIndex = attr.IndexOf('=');
|
||||||
if (ss.Length != 2)
|
string key = sepIndex >= 0 ? attr.Substring(0, sepIndex) : attr;
|
||||||
{
|
string value = sepIndex >= 0 ? attr.Substring(sepIndex + 1) : "";
|
||||||
throw new Exception($"单元薄 meta 定义出错. attribute:{attr}");
|
|
||||||
}
|
|
||||||
string key = ss[0].Trim();
|
|
||||||
string value = ss[1].Trim();
|
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case "orientation":
|
case "row":
|
||||||
{
|
{
|
||||||
orientRow = DefUtil.ParseOrientation(value);
|
orientRow = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "title_rows":
|
case "column":
|
||||||
{
|
{
|
||||||
if (!int.TryParse(value, out var v))
|
orientRow = false;
|
||||||
{
|
|
||||||
throw new Exception($"单元薄 meta 定义 title_rows:{value} 属性值只能为整数[{TITLE_MIN_ROW_NUM},{TITLE_MAX_ROW_NUM}]");
|
|
||||||
}
|
|
||||||
if (v < TITLE_MIN_ROW_NUM || v > TITLE_MAX_ROW_NUM)
|
|
||||||
{
|
|
||||||
throw new Exception($"单元薄 title_rows 应该在 [{TITLE_MIN_ROW_NUM},{TITLE_MAX_ROW_NUM}] 范围内,默认是{TITLE_DEFAULT_ROW_NUM}");
|
|
||||||
}
|
|
||||||
titleRows = v;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "table":
|
case "table":
|
||||||
|
|
@ -297,31 +255,56 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
throw new Exception($"非法单元薄 meta 属性定义 {attr}, 合法属性有: orientation=r|row|c|column,title_rows=<number>,table=<tableName>");
|
throw new Exception($"非法单元薄 meta 属性定义 {attr}, 合法属性有: row,column,table=<tableName>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryParseMeta(IExcelDataReader reader, out bool orientRow, out int titleRows, out string tableName)
|
public static bool TryParseMeta(IExcelDataReader reader, out bool orientRow, out string tableName)
|
||||||
{
|
{
|
||||||
if (!reader.Read() || reader.FieldCount == 0)
|
if (!reader.Read() || reader.FieldCount == 0)
|
||||||
{
|
{
|
||||||
orientRow = true;
|
orientRow = true;
|
||||||
titleRows = TITLE_DEFAULT_ROW_NUM;
|
|
||||||
tableName = "";
|
tableName = "";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var cells = new List<string>();
|
string metaStr = reader.GetString(0)?.Trim();
|
||||||
for (int i = 0, n = reader.FieldCount; i < n; i++)
|
return TryParseMeta(metaStr, out orientRow, out tableName);
|
||||||
{
|
|
||||||
cells.Add(reader.GetString(i)?.Trim());
|
|
||||||
}
|
|
||||||
return TryParseMeta(cells, out orientRow, out titleRows, out tableName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<List<Cell>> ParseRawSheetContent(IExcelDataReader reader, bool orientRow, int? maxParseRow = null)
|
private static bool IsSubFieldRow(List<Cell> row)
|
||||||
|
{
|
||||||
|
if (row.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var s = row[0].Value?.ToString()?.Trim();
|
||||||
|
return s == "##field";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTypeRow(List<Cell> row)
|
||||||
|
{
|
||||||
|
if (row.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var s = row[0].Value?.ToString()?.Trim();
|
||||||
|
return s == "##type";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsHeaderRow(List<Cell> row)
|
||||||
|
{
|
||||||
|
if (row.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var s = row[0].Value?.ToString()?.Trim();
|
||||||
|
return !string.IsNullOrEmpty(s) && s.StartsWith("##");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<Cell>> ParseRawSheetContent(IExcelDataReader reader, bool orientRow, bool headerOnly)
|
||||||
{
|
{
|
||||||
// TODO 优化性能
|
// TODO 优化性能
|
||||||
// 几个思路
|
// 几个思路
|
||||||
|
|
@ -330,7 +313,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
// 3. 跳过null或者empty的单元格
|
// 3. 跳过null或者empty的单元格
|
||||||
var originRows = new List<List<Cell>>();
|
var originRows = new List<List<Cell>>();
|
||||||
int rowIndex = 0;
|
int rowIndex = 0;
|
||||||
while (reader.Read())
|
do
|
||||||
{
|
{
|
||||||
++rowIndex; // 第1行是 meta ,标题及数据行从第2行开始
|
++rowIndex; // 第1行是 meta ,标题及数据行从第2行开始
|
||||||
var row = new List<Cell>();
|
var row = new List<Cell>();
|
||||||
|
|
@ -339,11 +322,11 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
row.Add(new Cell(rowIndex, i, reader.GetValue(i)));
|
row.Add(new Cell(rowIndex, i, reader.GetValue(i)));
|
||||||
}
|
}
|
||||||
originRows.Add(row);
|
originRows.Add(row);
|
||||||
if (orientRow && maxParseRow != null && originRows.Count > maxParseRow)
|
if (orientRow && headerOnly && !IsHeaderRow(row))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
} while (reader.Read());
|
||||||
|
|
||||||
List<List<Cell>> finalRows;
|
List<List<Cell>> finalRows;
|
||||||
|
|
||||||
|
|
@ -401,22 +384,22 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
private static RawSheetTableDefInfo ParseSheetTableDefInfo(string rawUrl, IExcelDataReader reader)
|
private static RawSheetTableDefInfo ParseSheetTableDefInfo(string rawUrl, IExcelDataReader reader)
|
||||||
{
|
{
|
||||||
bool orientRow;
|
bool orientRow;
|
||||||
int headerRowNum;
|
|
||||||
|
|
||||||
if (!TryParseMeta(reader, out orientRow, out headerRowNum, out var _))
|
if (!TryParseMeta(reader, out orientRow, out var _))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var cells = ParseRawSheetContent(reader, orientRow, headerRowNum);
|
var cells = ParseRawSheetContent(reader, orientRow, true);
|
||||||
var title = ParseTitle(cells, reader.MergeCells, orientRow, out int titleRowNum);
|
var title = ParseTitle(cells, reader.MergeCells, orientRow);
|
||||||
|
|
||||||
if (cells.Count <= titleRowNum)
|
int typeRowIndex = cells.FindIndex(row => IsTypeRow(row));
|
||||||
|
|
||||||
|
if (typeRowIndex < 0)
|
||||||
{
|
{
|
||||||
throw new Exception($"缺失type行");
|
throw new Exception($"缺失type行");
|
||||||
}
|
}
|
||||||
List<Cell> typeRow = cells[titleRowNum];
|
List<Cell> typeRow = cells[typeRowIndex];
|
||||||
List<Cell> briefDescRow = cells.Count > titleRowNum + 1 ? cells[titleRowNum + 1] : null;
|
List<Cell> descRow = cells.Count > typeRowIndex + 1 ? cells[typeRowIndex + 1] : null;
|
||||||
List<Cell> destailDescRow = cells.Count > titleRowNum + 2 ? cells[titleRowNum + 2] : briefDescRow;
|
|
||||||
|
|
||||||
var fields = new Dictionary<string, FieldInfo>();
|
var fields = new Dictionary<string, FieldInfo>();
|
||||||
foreach (var subTitle in title.SubTitleList)
|
foreach (var subTitle in title.SubTitleList)
|
||||||
|
|
@ -429,9 +412,8 @@ namespace Luban.Job.Cfg.DataSources.Excel
|
||||||
{
|
{
|
||||||
Name = subTitle.Name,
|
Name = subTitle.Name,
|
||||||
Tags = title.Tags,
|
Tags = title.Tags,
|
||||||
Type = typeRow?[subTitle.FromIndex].Value?.ToString() ?? "",
|
Type = typeRow[subTitle.FromIndex].Value?.ToString() ?? "",
|
||||||
BriefDesc = briefDescRow?[subTitle.FromIndex].Value?.ToString() ?? "",
|
Desc = descRow?[subTitle.FromIndex].Value?.ToString() ?? "",
|
||||||
DetailDesc = destailDescRow?[subTitle.FromIndex].Value?.ToString() ?? "",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Luban.Job.Cfg.Datas
|
namespace Luban.Job.Cfg.DataSources
|
||||||
{
|
{
|
||||||
public class Record
|
public class Record
|
||||||
{
|
{
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Luban.Common.Utils;
|
using Luban.Common.Utils;
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using Luban.Job.Cfg.Validators;
|
using Luban.Job.Cfg.Validators;
|
||||||
using Luban.Job.Common.Types;
|
using Luban.Job.Common.Types;
|
||||||
|
|
|
||||||
|
|
@ -311,17 +311,7 @@ namespace Luban.Job.Cfg.Defs
|
||||||
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{name}' type missing!");
|
throw new Exception($"table:'{table.Name}' file:{file.OriginFile} title:'{name}' type missing!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先取desc行,如果为空,则取title行
|
cf.Comment = f.Desc;
|
||||||
|
|
||||||
cf.Comment = f.BriefDesc;
|
|
||||||
if (string.IsNullOrWhiteSpace(cf.Comment))
|
|
||||||
{
|
|
||||||
cf.Comment = f.DetailDesc;
|
|
||||||
}
|
|
||||||
if (string.IsNullOrWhiteSpace(cf.Comment))
|
|
||||||
{
|
|
||||||
cf.Comment = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
cf.Type = attrs[0];
|
cf.Type = attrs[0];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Bright.Collections;
|
using Bright.Collections;
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
#if !LUBAN_LITE
|
#if !LUBAN_LITE
|
||||||
using Luban.Job.Cfg.l10n;
|
using Luban.Job.Cfg.l10n;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Luban.Job.Cfg.DataConverts;
|
using Luban.Job.Cfg.DataConverts;
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using Luban.Job.Cfg.Utils;
|
using Luban.Job.Cfg.Utils;
|
||||||
using System;
|
using System;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using Luban.Job.Cfg.DataConverts;
|
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;
|
||||||
using Luban.Job.Cfg.DataVisitors;
|
using Luban.Job.Cfg.DataVisitors;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using Luban.Job.Cfg.l10n;
|
using Luban.Job.Cfg.l10n;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Bright.Collections;
|
using Bright.Collections;
|
||||||
using Luban.Common.Utils;
|
using Luban.Common.Utils;
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using Luban.Job.Cfg.DataVisitors;
|
using Luban.Job.Cfg.DataVisitors;
|
||||||
using Luban.Job.Cfg.Defs;
|
using Luban.Job.Cfg.Defs;
|
||||||
using Luban.Job.Cfg.RawDefs;
|
using Luban.Job.Cfg.RawDefs;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Luban.Job.Cfg.Datas;
|
using Luban.Job.Cfg.Datas;
|
||||||
|
using Luban.Job.Cfg.DataSources;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue