解决了大部分开发者的痛点的 Excel 解析工具
商店地址: Excel to JSON | Cocos Store
工具简介
工具用于将 Excel 转为 json,并提供所有的代码提示。
.
├── Creator351 # 示例工程
├── bin # 此解析插件的 js 脚本
├── xlsx # 此插件的示例 Excel 文件集合
├── export # 插件导出的内容
├── json # 插件导出的 json 文件
└── script # 插件导出的脚本
├── node_modules # npm install 后的依赖项
├── package-lock.json # npm install 后的 lock 文件
├── package.json # npm 项目的 package.json
└── README.md # 说明文档
Excel 相关配置规则
具体包括以下内容:
-
解析将跳过以下格式的 xlsx 文件:
- 跳过扩展名不是 .xlsx 的文件;
- 跳过 ~ 开头的文件;
- 跳过 .~ 开头的文件(windows 下打开 Excel 产生的临时文件);
-
一个 Xlsx 文件支持多个 Sheet 的解析机制;Sheet 是否解析由其名字决定:
- 以 “~” 开头的 Sheet 不解析;
- Sheet 名称必须以 “@” 符号作为分隔符;
- @ 后面必须是纯字母或数字;
- @ 前面一般用于标记配置表的中文名称;
-
列解析原则:
- 只解析 “c”、“s”、“cs”、“sc” 开头的列,其余列忽略;(一般建议忽略的列,以 “//” 开头);
- 第一列的字段名必须为小写的 “id”;
-
行解析原则:id 段值为空或者以 // 开头的行都将忽略该行数据的解析;
-
Excel 表支持的字段类型(具体参见 xlsx 中的示例配置表):
number、string、int、
number[]、int[]、string[]、
number[][]、int[][]、string[][]、
{xx: number, yy:string}、
u|id:int#cnt:int#price:number、
u|id:int#cnt:int#price:number[]、
u|id:int#cnt:int#price:number[][] -
导出的 json 数据精简机制;json 数据进行了字段的压缩,降低了 json 数据的大小(具体可查看 export 文件夹中的数据,进行了解);
-
多语言表解析机制(可参考 xlsx 中关于多语言表的配置);
-
全局表解析机制(可参考 xlsx 中关于全局表的配置);
-
自动生成代码机制;(属性声明文件、配置表访问类、配置表管理器)字段名强类型提示功能(具体参考 Creator351 工程中的示例);
-
自动生成 id 枚举类并支持注释的机制(参考 IKey.ts 中的示例);
-
游戏使用示例工程(Creator 3.5.1)
初始化工具
在当前目录下执行以下命令,在相关依赖安装完毕后,就可以使用 Excel 解析工具了
npm install
插件如何使用
- 生成 json 和 script 到 export 目录
# 使用 package 定义的指令
npm run demo
# 实际调用的是以下指令(二者等价)
node ./bin/main.js -d -s ./xlsx -o ./export/json/ --out-script-path ./export/script/
- 生成 json 和 script 到 ./Creator351/assets/xlsx 目录
# 使用 package 定义的指令
npm run cocos
# 实际调用的是以下指令(二者等价)
node ./bin/main.js -d -s ./xlsx -o ./Creator351/assets/xlsx/json/ --out-script-path ./Creator351/assets/xlsx/script/
以下是一次解析控制台示例
➜ store git:(master) npm run demo
> excel_to_any@1.0.0 demo
> node ./bin/main.js -d -s ./xlsx -o ./export/json/ --out-script-path ./export/script/
{
debug: true,
srcPath: './xlsx',
outJsonsPath: './export/json/',
outScriptPath: './export/script/'
}
>> xlsx 所在目录路径 : /Users/z/Desktop/excel_to_any/store/xlsx
>> 输出 json 路径 : /Users/z/Desktop/excel_to_any/store/export/json
>> 输出 script 路径 : /Users/z/Desktop/excel_to_any/store/export/script
>> 开始解析 xlsx 文件: /Users/z/Desktop/excel_to_any/store/xlsx/示例表.xlsx
开始解析: 示例表@demo
>> 跳过 (~ 开头) : ~测试表@demo2
>> 开始解析 xlsx 文件: /Users/z/Desktop/excel_to_any/store/xlsx/多语言表.xlsx
开始解析: 多语言表@Lang
>> 开始解析 xlsx 文件: /Users/z/Desktop/excel_to_any/store/xlsx/全局配置表.xlsx
开始解析: 全局表@g
生成多语言文件: Lang-zh-cn.json
生成多语言文件: Lang-en.json
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 解析完成!!!
游戏中如何使用
参考 Creator351 工程的使用示例即可。
主要包含几个内容:
- xlsx 实例对象。用于将加载后的 json 数据进行注册;各个 Sheet 表访问的入口;全局表访问的入口;
- lang 实例对象。用于多语言的配置表数据添加、多语言的切换;多语言文本的访问;自动替换多语言中的变量的机制;
import { assetManager, AssetManager, Component, JsonAsset, _decorator } from 'cc';
import { id_Lang } from './xlsx/script/IKey';
import { lang } from './xlsx/script/lang';
import { xlsx } from './xlsx/script/xlsx';
const { ccclass, property } = _decorator;
/**
* xlsx 工具导出的 json 文件清单格式
*/
type IManifestJson = {
data: {
key: string,
name: string,
}[]
}
/**
* 这是游戏的入口脚本,挂载在 EntryScene 的 Canvas 中
*/
@ccclass('EntryScene')
export class EntryScene extends Component {
async start() {
/**
* assets/xlsx 被标记为一个 bundle 包,xlsx 是其包名;
* 因此要先加载该 Bundle 包
*/
const xlsxBundle = await new Promise(resolve => {
assetManager.loadBundle("xlsx", (err, bundle: AssetManager.Bundle) => {
if (err) {
console.log(`excel_to_any >> 加载 bundle 包 xlsx >> 失败`, err);
resolve(null);
return;
}
console.log(`excel_to_any >> 加载 bundle 包 xlsx >> 成功`);
resolve(bundle);
});
});
/** 如果加载 xlsx 包失败,直接返回 */
if (!xlsxBundle) {
return;
}
/**
* 加载 xlsx 导出的 json 文件清单。 注意:
* 1、json/_manifest 是相对于包 xlsx 的文件路径(加载 json 文件不需要指定其扩展名 .json)
* 2、IManifestJson 是清单中的数据格式
*/
const _manifest = await this.LoadJsonAsync<IManifestJson>("json/_manifest", "xlsx");
/** 根据清单,加载所有配置表(除了多语言) */
for (let i = _manifest.data.length - 1; i >= 0; --i) {
/** 加载配置文件 */
const jsonData = await this.LoadJsonAsync(`json/${_manifest.data[i].key}`, "xlsx");
console.log(`加载 ${_manifest.data[i].name} 表成功`);
xlsx.AddXlsx(_manifest.data[i], jsonData);
}
console.log(` ----------------- 普通表测试 ----------------- `)
console.log(`demo >> `, xlsx.demo.row(100))
console.log(`demo >> id: `, xlsx.demo.row(100).id)
console.log(`demo >> name: `, xlsx.demo.row(100).name)
console.log(`demo >> strList: `, xlsx.demo.row(100).strList)
console.log(`demo >> strList2: `, xlsx.demo.row(100).strList2)
console.log(`demo >> intTyp: `, xlsx.demo.row(100).intTyp)
console.log(`demo >> intList: `, xlsx.demo.row(100).intList)
console.log(`demo >> intList2: `, xlsx.demo.row(100).intList2)
console.log(`demo >> numTyp: `, xlsx.demo.row(100).numTyp)
console.log(`demo >> numList: `, xlsx.demo.row(100).numList)
console.log(`demo >> numList2: `, xlsx.demo.row(100).numList2)
console.log(`demo >> id_cnt_price: `, xlsx.demo.row(100).id_cnt_price)
console.log(`demo >> id_cnt_price_arr: `, xlsx.demo.row(100).id_cnt_price_arr)
console.log(`demo >> id_cnt_price_arr2: `, xlsx.demo.row(100).id_cnt_price_arr2)
console.log(` ----------------- 全局表测试 ----------------- `)
console.log(`g >> intArr2Val: `, xlsx.g.intArr2Val);
console.log(`g >> intArrVal: `, xlsx.g.intArrVal);
console.log(`g >> intVal: `, xlsx.g.intVal);
console.log(`g >> json1: `, xlsx.g.json1);
console.log(`g >> userVal: `, xlsx.g.userVal);
console.log(`g >> userArrVal: `, xlsx.g.userArrVal);
console.log(`g >> userArr2Val: `, xlsx.g.userArr2Val);
console.log(` ----------------- 多语言表测试 ----------------- `)
/** 语言包单独加载 */
const _lang_zh_cn_Json = await this.LoadJsonAsync<IManifestJson>("json/Lang-zh-cn", "xlsx");
/** 添加语言包 */
lang.AddLang("zh-cn", _lang_zh_cn_Json);
/** 设置当前语音包 */
lang.SetLang("zh-cn");
/** 打印中文 */
console.log(lang.text(id_Lang.com_confirm));
console.log(lang.text(id_Lang.cost_n, { n: 99 }));
const _lang_en_Json = await this.LoadJsonAsync<IManifestJson>("json/Lang-en", "xlsx");
/** 添加语言包 */
lang.AddLang("en", _lang_en_Json);
lang.SetLang("en");
/** 打印英文 */
console.log(lang.text(id_Lang.com_confirm));
console.log(lang.text(id_Lang.cost_n, { n: 88 }));
}
/**
* 定义如何加载 json 文件的方法(一般框架级别会有自己的资源管理解决方案,可自行替换)
* @param jsonPath json 文件所在 bundle 中的路径
* @param bundleKey bundle 名称
* @returns
*/
private LoadJsonAsync<JSON_DATA>(jsonPath: string, bundleKey: string): Promise<JSON_DATA> {
return new Promise(resolve => {
const bundle = assetManager.getBundle(bundleKey);
bundle.load(jsonPath, JsonAsset, (err, asset) => {
if (err) {
console.error(err);
resolve(null);
return;
}
resolve(asset.json as any as JSON_DATA);
});
});
}
}